How to add case insensitive where in Laravel
Solving the Repetitive Query Dilemma: Harnessing the Power of Laravel Eloquent Macros
Have you ever found yourself writing the same complex database queries repeatedly? Yeah, me too. It’s frustrating, time-consuming, and, let’s face it, not the best use of our coding superpowers.
I needed to search some tables for case-insensitive values and discovered that Laravel doesn’t have a native function in eloquent, or it’s entirely possible that I missed it. Today, we will tackle this problem head-on by diving into the world of Laravel Eloquent macros. If you’ve never used them, this will be a game-changer for your productivity.
The Problem: Query Repetition Syndrome
Picture this: You’re working on a project where you frequently need to perform case-insensitive searches across multiple columns. Or maybe you’re constantly writing complex ordering logic to handle null values. Sound familiar?
If you’re nodding your head, you’re not alone. This is what I call “Query Repetition Syndrome,” and it’s a common ailment among Laravel developers.
The Solution: Eloquent Macros to the Rescue
Enter Eloquent macros – your new secret weapon against repetitive queries. These bad boys allow you to extend Eloquent’s query builder with custom methods. It’s like giving your Laravel app query superpowers!
Let’s break down how to implement this solution step-by-step.
Step 1: Create Your Macro Factory
First things first, we need a place to house our macros. Let’s create a new service provider:
php artisan make:provider MacroServiceProvider
Step 2: Set Up the MacroServiceProvider
Open up that freshly minted MacroServiceProvider.php
, and let’s give it some structure:
<?php
namespace App\Providers;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Arr;
class MacroServiceProvider extends ServiceProvider
{
public function boot(): void
{
$this->addWhereInLikeMacro();
$this->addWhereLikeMacro();
$this->addOrderByNullsLastMacro();
}
// We'll add our macro methods here
}
Step 3: Craft Your First Macro
Let’s tackle that case-insensitive search problem with a whereInLike
macro:
protected function addWhereInLikeMacro(): void
{
Builder::macro('whereInLike', function ($column, array $values, $caseSensitive = false) {
return $this->where(function ($query) use ($column, $values, $caseSensitive) {
$column = DB::raw($caseSensitive ? $query->getGrammar()->wrap($column) : 'LOWER(' . $query->getGrammar()->wrap($column) . ')');
foreach ($values as $value) {
$method = $query->getQuery()->wheres ? 'orWhere' : 'where';
$value = $caseSensitive ? "%$value%" : '%' . strtolower($value) . '%';
$query->$method($column, 'LIKE', DB::raw('?'));
$query->addBinding($value, 'where');
}
});
});
}
Step 4: Add More Macros to Your Arsenal
While we’re at it, let’s add a couple more handy macros:
protected function addWhereLikeMacro(): void
{
Builder::macro('whereLike', function ($columns, $value) {
return $this->where(function ($query) use ($columns, $value) {
foreach (Arr::wrap($columns) as $column) {
$query->orWhere(DB::raw('LOWER(' . $query->getGrammar()->wrap($column) . ')'), 'LIKE', '%' . strtolower($value) . '%');
}
});
});
}
protected function addOrderByNullsLastMacro(): void
{
Builder::macro('orderByNullsLast', function ($column, $direction = 'asc') {
$column = $this->getGrammar()->wrap($column);
$direction = strtolower($direction) === 'asc' ? 'asc' : 'desc';
return $this->orderByRaw("CASE WHEN {$column} IS NULL THEN 1 ELSE 0 END, {$column} {$direction}");
});
}
Step 5: Register Your Macro Provider
Don’t forget to tell Laravel about your new macros. Add this line to your config/app.php
:
'providers' => [
// Other providers...
App\Providers\MacroServiceProvider::class,
],
Step 6: Unleash Your New Query Superpowers
Now for the fun part – using your new macros:
// Case-insensitive search for multiple values
$users = User::whereInLike('name', ['John', 'Jane', 'Bob'])->get();
// Search across multiple columns
$users = User::whereLike(['name', 'email'], 'john')->get();
// Order results with nulls last
$users = User::orderByNullsLast('last_login')->get();
The Result: Clean, Efficient, and DRY Code
By implementing these Eloquent macros, we’ve solved our Query Repetition Syndrome. Instead of writing the same complex queries over and over, we have reusable, eloquent (pun intended) methods that make our code cleaner and more efficient.
But here’s the kicker – this is just the beginning. You can create macros for any repetitive query pattern in your projects. The possibilities are endless!
Wrapping Up
Eloquent macros are like a Swiss Army knife for your database queries. They’re powerful and flexible and can save you tons of time and headaches.
Remember, the goal here is to work smarter, not harder. By identifying repetitive patterns in your queries and turning them into macros, you’re setting yourself up for cleaner, more maintainable code in the long run.
Now, I’m curious – what repetitive query patterns have you encountered in your Laravel projects? Please drop a comment below to share your macros or share some macro solutions!
Happy coding, and may your queries be ever elegant!
Deploying Storybook via Gitlab Pipeline via SFTP
This is an update to my previous script to deploy Storybook to S3 via Gitlab Pipelines. We’re running a dev server on EC2 and I wanted to be able to deploy the project using SFTP. This assumes you already created a new user account on the FTP server.
Create an SSH key on your local machine and save it without a password using the following command:
sh-keygen -t ed25519 -C "GitLab SSH key"
You should have two new files in .ssh
directory:
id_ed25519
— private keyid_ed25519.pub
— public key
Add Key to Gitlab CI/CD Settings
Copy content of private key and go back to GitLab project. Navigate to Settings -> CI/CD -> Variables -> Expand -> Add Variable
. GitLab’s variable is a key-value pair. Name key SSH_PRIVATE_KEY
and paste private key in value field. Click Add Variable
.
Add two more variables:
SSH_USER
— name of the user on the remote serverSSH_HOST
— IP address of remote server- FTPFOLDERPATH – The path where to upload files too on your server.
Copy the contents of public key and go back to remote server. Login as the same user which you have specified in SSH_USER
GitLab’s variable.
Add SSH Key to Server
Navigate to /home/<username>/.ssh
. If directory .ssh
doesn’t exist, then create it. Paste the public key into authorized_keys
file. If you don’t have authorized_keys
file, create it.
Add .gitlab.ci.yml
Important Note: I ended up using node:16 for my script because my instance of Storybook was using Webpack 4 and the node:latest was using Node 18 which wasn’t compatible. If you’re using webpack 5, you should be able to use a later version. I’m also using node:16 instead of alpine or buster because I ran into issues adding the SSH keys. I’ll work on optimizing this later for performance but it works.
image: node:16
stages:
- build
- deploy
build:
stage: build
script:
- npm install
- npm run build-storybook
artifacts:
paths:
- storybook-static
only:
- develop
- main
deploy:
stage: deploy
artifacts:
paths:
- storybook-static
before_script:
- "command -v ssh-agent >/dev/null || ( apt-get update -y && apt-get install openssh-client -y )"
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
- ssh-keyscan $SSH_HOST >> ~/.ssh/known_hosts
- chmod 644 ~/.ssh/known_hosts
- ls -la
script:
- echo "$SSH_USERNAME@$SSH_HOST"
# clean up old storybook files
- ssh -p22 $SSH_USERNAME@$SSH_HOST "rm -rf $FTPFOLDERPATH"
# deploy artifacts
- scp -P22 -r storybook-static/* $SSH_USERNAME@$SSH_HOST:$FTPFOLDERPATH
only:
- develop
- main
SourceTree Crashes on Load
I’ve recently started using Git for development and while I am getting used to the command line tools on Windows, I have been using Atlassian’s free tool, SourceTree to help manage my code. Recently I’ve run into an issue where SourceTree crashes on start up.”Oh dear. We’re sorry, but SourceTree just crashed. How terribly embarrassing.”
I submitted an issue (https://jira.atlassian.com/browse/SRCTREEWIN-3011) and received a response that didn’t solve the issue. I submitted a follow up but never heard back so I did some further digging and found that SourceTree was crashing because it corrupted a repo and was still trying to load it back up on start up. The application does not exit gracefully or even give you the option to start in a safe mode which loads nothing.
To prevent it from opening the repo on start up:
Open %localappdata%/Atlassian/SourceTree/opentabs.xml in notepad or another editor.
Delete the problem repo.
Viola! No more SourceTree crashes on start up.
How to Capture Google Analytics Before Redirect
Up until today, everyone I know has struggled to capture a Google Analytics event on redirect links. We’ve all had to come up with hacks and workarounds to ensure that the initial landing page is tracked. Today I finally figured out a solution that works. Google analytics now offers a callback event which lets you fire the redirect (or any custom functionality after the main tracking event fires.
The snippet to add after the Google analytics tracking code setup is below:
ga('send', 'pageview', { 'page': '/my-new-page', 'hitCallback': function() { window.location.replace = "[new url]"; } });
Reading JSON through JQuery from Cross Domain ASP.NET Web Service
Recently I had an issue with JQuery and accessing JSON from a cross domain ASP.NET Web Service. After much googling, I stumbled upon many articles that provided no fix that would solve the issue.
Every sample I found was some derivative of the following code:
$.ajax({ type: 'POST', dataType: 'jsonp', contentType: "application/json; charset=utf-8", , url: 'http://www.domain.com/webservice.asmx/function', data: '{}', success: function (response) {} });
Nearly every post pointing out that the contentType argument was the issue but it still didn’t work when I included it. There were posts that said you can’t use GET and had to use POST. There might be valid security issues with not using GET but that’s another topic of discussion. in the case of an open web service where you’re providing raw data to be consumed, a GET should suffice just fine.
To support GET, you need to add the following attribute tags to your asmx.cs:
[sourcecode language=”csharp”][WebMethod(), ScriptMethod(UseHttpGet = true, ResponseFormat = ResponseFormat.Json)][/sourcecode]
This will cause ASP.NET to automatically serialize the returned data to JSON without requiring you to do it manually in code. There are no issues when making the call locally either. The second you go cross domain, the call fails.
A few articles mention JSONP (JSON with Padding) which is supposed to provide a workaround for the Same Origin Policy in JavaScript. Once I implemented the JSONP, the entire function
function getJSON() { var url = 'http://www.domain.com/webservice.asmx/function'; $.ajax({ type: 'GET', url: url, async: false, jsonpCallback: 'jsonCallback', contentType: "application/json", dataType: 'jsonp', success: function (json) { alert(json); }, error: function (e) { alert(e.toString()); } }); }
Android/Printer/Windows7 Won’t Connect to WiFi
The Problem
I recently ran into an issue where random devices wouldn’t connect to my WiFi while others could. None of my android devices could connect including my phone and tablet but most Windows 7 devices could. A friend brought her Windows 7laptop over and was unable to connect it to my WiFi.
I just spent two weeks troubleshooting the issue which should have been more obvious. At first I thought it was because I had setup the encryption as WPA2 and the devices didn’t
It was definitely a stupid mistake on my part but one that is easily overlooked.
The Solution
Check your WiFi settings and verify the mode. It turns out I had set the router to Wireless N only and the devices that couldn’t connect only supported up to Wireless G. I switched the router to Wireless G/N and all the devices started working.
On Verizon’s router, go to Wireless Settings -> Advanced Security Settings
Under Level 3, you’ll see “802.11b/g/n Mode”.
Round Up to Whole Numbers in Excel (10s, 100s, 1000s, etc)
After years of using Excel, I realized today I have never had to round up to the nearest whole number before – until today that is. I was organizing my finances and realized that I wanted to round some of the amounts up to the nearest 10. So to round to the nearest decimal place in Excel, the formula is:
=ROUNDUP([Range],[Position])
Count the number of places after the 0 to round to and set [Position] to that value
So if you had 1234.25 in Cell A1 and wanted to round up to the nearest cent, =ROUNDUP(A1, 1) would produce 1234.30.
1234.25 ^ 1 |
1234.25 ^ 2 |
=ROUNDUP(A1,1) | =ROUNDUP(A1,2) |
Want to go the other way? Simply start at the decimal as 0 and count backwards in the negated position.
1234.25 ^ -1 |
1234.25 ^ -2 |
1234.25 ^ -3 |
1234.25 ^ -4 |
=ROUNDUP(A1, -1) | =ROUNDUP(A1, -2) | =ROUNDUP(A1, -3) | =ROUNDUP(A1, -4) |
To round to the nearest ten (10)
To round to the nearest hundred (100)
To round to the nearest thousand (100)
SSL, jQuery, and CDN
I just got whacked by a minor bug with SSL and the Google CDN (totally my fault, not theirs). I stuck the reference to the CDN in my master page not realizing one of the pages would be served up as secured by the vendor due to compliance issues. It made it through all testing because none of the staging/dev environments were configured for SSL and I was not made aware of the fact that we’d be serving the page up through SSL. Internet Explorer 8 prompted users about the insecure content before rendering the page. In their infinite wisdom, Microsoft decided to implement a new workflow for insecure content where the content is ignored and the page renders immediately with the unsecured content ignored. Since jQuery was used on multiple parts of the form, the site essentially broke. Google Chrome and Firefox seem to recognize the CDN as a trusted source and render the page as expected.
To fix the site, I added a javascript check to set the appropriate prefix to the CDN call:
<script>// <![CDATA[ var gaJsHost = (("https:" == document.location.protocol) ? "https://" : "http://"); document.write(unescape("%3Cscript src='" + gaJsHost + "ajax.googleapis.com/ajax/libs/jquery/1.4.3/jquery.min.js' type='text/javascript'%3E%3C/script%3E")); // ]]></script>
“This is BMW. We don’t negotiate.”
Following my ordeal with Ford, I started my drive home and drove past a BMW dealership. I decided to check it out and pulled into the lot, parked, and walked inside. My mother and I walked around looking at the cars and nobody walked over to help for a good 15 minutes. Finally I walked over to the front desk and the girl looked up and asked what I wanted. I said “I’m looking to purchase a BMW.” She suddenly had a smile on her face and said “OH! One second, let me see whom I can get to help you.”
The girl made a phone call and spoke softly and then followed by “uh huh. Ok.” and then looked up to me and said “One of the sales reps will be right with you.” I nodded and gave her my thanks and walked back to the car I was looking at. I couldn’t help but feel like the lack of anyone coming to help and her initial attitude was caused by some sort of racial profiling but I could totally be off base on that (but based on prior experiences, I’m more than likely correct). Finally a salesman came out, introduced himself and then asked how he could help. I explained I was looking for a 328xi to which he responded we should step into his office. He started filling out some paperwork to which I stated “Wait, we haven’t discussed anything yet. Why are you filling out forms already?” to which he replied ”This will save time. You obviously seem like you’ve decided on the car you want.” I eyed him suspiciously and after he filled in the paperwork, he excused himself and took the paperwork and walked away. I looked at my mother and whispered “I seriously think he’s running a credit check to see if I can get the car.” We were both agitated already from our previous experience not 30 minutes prior. I’ve purchased a few cars in the past, for both myself and my parents and have never encountered the type of obnoxious behaviors I’ve run into that day.
So he comes back, sans paperwork and sits down. He starts asking me what I’m looking for and is interrupted by a phone call. He says “Excuse me one second, that’s my wife.” and I happened to see that the phone was an extension internally. I listened very carefully and heard “The credit is seriously excellent. They can buy the 7 series if they wanted.” to which he replied “Wonderful! OK, I have to go, I’ll talk to you later.” and turned to us. “Sorry, she wanted to let me know dinner would be waiting for me when I got home.”
I’m going to shorten the story but we discussed what I wanted, and he said he had the car in the lot and made me an offer. I countered and he looked at me and said “This is BMW. We don’t negotiate.” I was totally surprised and countered with “I know for a fact that’s not true as four people I know negotiated before they bought their car.” He said “They might have got a good special, but the best I can do for you is give you a 1.99% financing. It ends tomorrow so we have to sign today.” I said “If I sign, how long do I have before I can cancel the deal with no penalties?” He said “You have until you take delivery.”
“Let’s go look at the car.” I got in and drove it a bit, and it handled great. Even my mother was impressed how it handled, but it had no options or features. I wasn’t so thrilled since I was giving up everything I actually wanted and not getting a great deal. In the beginning, I told him I wanted one thing no matter what – a hookup for my iphone to connect to the Bluetooth and to play music through the radio. If I have to compromise, that’s the one thing I refuse to give up. We talked as I drove the car around the lot a bit and he made comments about it’s power, acceleration, etc. Finally I got out of the car and began to look for the hookup for the iphone. When he inquired as to what I was searching for, I said “The iphone hookup which you guaranteed came with the car. I don’t see it anywhere.”
He said “It’s right…” and opened the center console followed by “uh-oh”. “Wow…ok…looks like the car doesn’t have the connection after all…not a big deal. You can get an aftermarket part. Let’s go finish the paperwork.”
I looked at him and point blank said “No. The deal’s off. I told you flat out that I’m giving up every option I wanted with this car. I’m settling for a lesser car and the ONE thing I REFUSE to give up is the one thing you guaranteed to be included. Now you’re telling me it’s not and I have to spend MORE money? I’m done.”
He replied “You’d really walk away from the deal for just this?”. “I already said deal’s off.” “OK, OK. Let me go talk to my boss. See what I can do.”
We go inside and he walks inside the manager’s office. He says “I have a customer who wants to purchase a car but he wants to hook up an iphone to it. The car is supposed to have the part but doesn’t. He’s ready to walk away on the deal.” I heard the manager say “For a $30 cable? Give it to him!”
The sales guy comes out and says “I convinced my manager to give you the part. It’s a $650 part that we’re giving you for free.”
We walked back to the office and sat down to sign the paperwork. Again I asked, “Just to double check, if I sign, how long do I have before I can cancel the deal with no penalties?” He said “I’m so confident you’re going to want the car, that you can break it until you take delivery of the vehicle.”
I signed the paperwork and told my friend what happened. He had been researching cars with me and said I should check out the Hyundai Genesis. He went with me to test drive the car the following Monday. We were both seriously impressed with the car and how it handled. I told the salesman that I’m looking to pay X for all the features and I don’t really need the car. If he can meet it, I’ll sign and drive away. If not, no hard feelings and I’ll walk away. He said “Wow, that’s low…but let me see what I can do.” He went to talk to his manager and came back after about 10 minutes. He said “My manager is calling the bank to see if they’ll approve it. It’s a real low offer and he doesn’t think they’ll approve but we never know.” Five minutes later, the manager comes over, shakes my hand and says “Congratulations. I hope you really enjoy your car. It’s the pride of our fleet.”
I called the BMW dealership and spoke to the salesman to let him know I was cancelling the deal. He responded “What can I do to keep your business?” I said “Honestly? There’s not much. You’re not going to beat the price.” He said “What kind of car are you getting and how much are you paying monthly?” I said “Hyundai Genesis and x dollars.” He said “You’re giving up a BMW for a HYUNDAI?” in shock. I chuckled and said “Not just any Hyundai, one that ranks higher than yours, is cheaper, more room in every direction, has every option you offer, and uses regular gas instead of premium.” He said “Is there anything that I could do to make you reconsider?” to which I calmly replied “Sorry, you’re BMW. You don’t negotiate.”
Wherein buying American is not an option
I will freely admit it – I’m an impulsive buyer. I get an idea into my head and it just cycles through the gears and synapses of my grey matter until I am finally forced to act upon said impulse. I’ve learned to temper and control these impulses recently but sometimes I can’t talk myself out of a purchase. My first car back in 99 was a Pathfinder. Oh how I LOVED that car. The memories I have of cruising around with my friends and the shenanigans we pulled will be something I treasure until the day I die (or I lose my memory, whichever happens first).
After about 5 years and 75,000 miles on the Pathfinder, it started to break down. First it was minor things but then suddenly I was in the shop on a monthly basis paying $500 each time. My transmission fell apart and I ended up shelling out close to $2200 if I remember correctly. The next month was $500 struts and shocks. My mechanic then commented that he’s never seen what happened to my shocks happen before. He also then informed me that it seems that it’s not just my Pathfinder breaking down. Everyone who has the same model/year of my car was experiencing the same problems. I just seemed to be the first and he could count on at least 5 more people bringing their car in. At that point I realized I could buy a new car and pay less a month than I was for the repairs. I got an ad in the mail for the Scions. I checked it out and the car was uncomfortably, ridiculously small. I ended up wedging my knee in between the steering and pedal. I seriously couldn’t move it at that point and I’m not even that tall. I’m average height. I ended up checking out the other cars and because they gave me a great deal, I ended up going with the Toyota Camry.
I will state for the record that I totally hated the car. While it was reliable, it had nothing desirable. It had no cool features, it had the same body style since 1992 (which my mom had) with a minor tweak here and there and a poorly designed interior. There was so much empty space and poor designing that it aggravated me every time I got into the car. Going from the Pathfinder to the Camry was a serious step down. I wanted the luxury edition of the Camry but I wanted the 4 cylinder as I was looking to save money gas. The gas prices at this time were hitting $4 a gallon and the hybrids weren’t so great for an option at the time. The dealer at that time told me that I can’t get the luxury package on the 4 cylinder as it was only available on the V-6 so I settled and purchased the 4 cylinder. I drove the car for a little over 5 years. It was reliable, but it didn’t excite me or particularly enthrall me in any way shape or form.
Recently I got it in my head that I should get a new car. It was one of those thoughts I cast aside as it was just not feasible with the apartment situation and the job and the commute I was doing. The thought would come back up periodically. I just wanted to drive something a little more exciting. I helped my friend move and drove his Infiniti to his place while he drove the truck. It was a totally different experience than my Camry or Pathfinder. The strangest part of the experience were the two or three cute women who waved at me and smiled as I drove past in his car – something that had never happened to me before in any car I’ve been in.
Fast forward a about a year and I moved back home. I did the commute to work again and it was horrible. The drives were over an hour and a half and with traffic up to 2. Then came the new job unexpectedly. It let me work from home and I went into the office only occasionally. Then out of the blue, the idea cycled back into my head. I ran it by mother because it was a large purchase and I wasn’t sure if I should do it. She surprised me with “Do it. You should be driving a better car than a Camry.” which totally surprised me. So I did research and looked at different brands. There was no major need for a new car since my current one ran fine so I wasn’t pressured to actually buy something right away.
I narrowed it down to these cars (in the order I wanted them originally):
- BMW 328xi
- Ford Taurus 2010 Select
- Ford Fusion Hybrid
- Chevy Camaro Transformers Edition
- Buick Lacrosse
- Hyundai Genesis
- Nissan Maxima
I started off with the Ford primarily for 2 reasons – the first being that with the terrible economy, it made more sense to me to support America. Secondly, they have two cars that I am interested in – the Ford Fusion Hybrid and the Ford 2010 Taurus. The cars had appeals for me in totally different ways. The Hybrid was great because it ran up to 47 mph on full electric (What limitation did they run into that they couldn’t bring it up to 55 I wonder?). The test drive was impressive. I hit the button and my friend and I looked at each other and I shrugged because nothing happened. I restarted the engine and the salesman was like “The car’s on Bro. It’s hybrid, it doesn’t make any noise until the gas kicks in.”
Needless to say, both my friend and I were seriously impressed. We test drove it and drove it and it handled fairly well, but it was nothing extravagant or special though. It was a practical ride with some luxury. For a technology buff such as myself, it was a fun toy. Then I test drove the Taurus and to sum it up, Ford finally got a car right. The Taurus was impressive in all aspects – design, handling, speed, power, space, features, and safety. It felt a little underpowered compared to it’s weight when I test drove it as the acceleration was a little slow. It turned out that it was a safety feature that parents can use for their kids to limit how fast they go through a governor. I decided I wanted the car and asked for pricing.
Things started off lousy when they told me the Taurus had 0% financing but the Fusion doesn’t. They wanted to steer me towards the more expensive car. I totally get it – however, I dislike being jerked around, especially when it comes to business. I told the sales manager “Listen, I don’t need the car. I have a perfectly fine car sitting outside. I want a new one. If you’re not going to give me a good deal, I have no problem walking. I just dislike wasting time.”
The sales manager replied “Sorry, that’s the deal we have going on now.” to which I replied “OK, thanks for your time. If I’m interested, I’ll come back.” to which they replied “Wait, let’s see what we can do.” They talked for a while amongst themselves and came back and gave me a price which was higher than MSRP. I said “OK, when you get your act together and are serious about doing business, call me.” and got up and walked out.
I got home and called two other Ford dealerships and requested quotes both via email and phone calls. One guy returned my call and told me he’s heading home but would get the quote out the next day. I told him if he gets me a good price, I’d be in the first thing in the morning to sign the deal. He said he’d get a quote out to me. Of course, I heard nothing back from them. I told my parents about my decision to buy a Ford and the reaction was a typical Indian parent’s response. Summed up – “NO FORD! NO AMERICAN CARS! THEY’LL KILL YOU!”
I took my mother with me to check out the car as my dad was so against me getting it that he didn’t even want to see it. I got to the same dealership and the same salesman saw me. “Ready to sign bro?!” I said “I want to show my mother the car first. I need to convince her it’s a good car.” I’ll admit, the next part shouldn’t have shocked me, but it did. “BRO! You already test drove the car! You don’t need to drive it again. I got five clients coming in today, I don’t have time for this.” I’m looking to buy the highest end car you have and you don’t have time for me? Even though I called ahead and told you I was coming in?
I stood straight up and said “Alright, I understand. I’ll just go somewhere where they have time for me. Thanks.” and turned to walk. “WAIT, WAIT! I’ll get the keys.”
He had already made a poor impression on my mother and she wasn’t thrilled with spending more time there but she waited it out. We got in the car and took a look through it. She was impressed by the quality, comfort, and space of the car. She still wasn’t thrilled with buying American but if I wanted to spend the money on it, it was mine to spend. Then we went to negotiate and it turned into a huge runaround ending with the guy arguing with me that the car on the lot was the model I wanted and me arguing it wasn’t. I wanted the 303 package and the car was the 302. “BRO! IT’S THE CAR YOU WANT!” “It’s the 302 package. It’s different from the 303 and is missing features I want. It’s not the same car. Here, look at the printout from YOUR website and compare. It’s missing seven features.” “BRO! IT"S THE SAME CAR!!”
I finally said “Are you seriously going to sit here and argue with me after I’ve proved it’s not the same car??” to which he responded by calling his manager over and said “Tell this guy that this is the car he wants. He won’t believe me.” The manager seemed puzzled by the request and said “Well what are you looking for?” to which I replied “I want the 303 package.” The manager glanced at the paper and said “The car we have is the 302, it’s different from the 303.” The salesman piped up “No, it’s the same car!” The manager looked at him and said “No, the 303 is a different package. It’s premium and costs more.” to which the salesman went quiet and turned bright red. “We don’t have the 303 in stock though. Is that what you’re looking to sign?”
I replied “Yes – if you can give me a good deal on it.” to which he replied “OK, let me see what I can find.” and walked away. The salesman walked with him and my mother quietly asked “Are you sure you want to do this? They don’t seem to know what they’re doing.” I was certain until this crap all happened. I had already been there 1-1/2 hours and haven’t even received a quote. I said “15 more minutes and we walk.” she didn’t say anything else. Then the salesman came back, sat down, and said “My manager’s looking into what we can get. You excited about this car? It’s a real beauty!” With a sinking feeling, I said “We’ll see. It all depends on the deal you give me.”
The manager came back and said “I got one located. Here’s the deal I can give you.” and handed me the paper. I looked at him and said “Seriously? This is the best you can do?” I can’t remember the exact cost now but it was something like $20 under MSRP. I said “This isn’t even a realistic starting point for discussion. let me know when you get a real deal to talk. I’m going to check out other cars now. I’ve wasted enough time here.” and got up to leave.
The salesman said “Bro, time is money. I’ve spent a lot of time with you and it’s only fair you give me a chance to get your business.” to which I replied “I already gave you two. By the way, good luck with the other five other clients you had coming in since none of them are here and you close in one hour.” and left.