Skip to content

Instantly share code, notes, and snippets.

@FatBoyXPC
Last active August 3, 2017 14:24
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save FatBoyXPC/b99c0c1f545dc6e12129becf5d0970b9 to your computer and use it in GitHub Desktop.
Save FatBoyXPC/b99c0c1f545dc6e12129becf5d0970b9 to your computer and use it in GitHub Desktop.
<?php
namespace App\Traits\Database;
trait MaybeTrait
{
/**
* I found myself doing something similar to this:
* $results = Company::where('type', $type)
*
* if ($region) {
* $results->where('region', $region)
* }
*
* $results->get();
*
* But that lends itself to the when() method:
*
* Company::where('type', $type)
* ->when($region, function ($query) {
* $query->where('region', $region);
* })
* ->get();
*
* However, I'm not a fan of that boilerplate for every "maybe". With this
* trait, we can instead do this:
*
* Company::where('type', $type)
* ->maybeWhere($region, 'region', $region)
* ->get();
*
* Where the first param when calling a "maybe" method is a predicate
* function.
*/
protected function maybeScope($query, $predicate, $method, $args)
{
return $query->when($predicate, function ($q) use ($method, $args) {
return $q->{$this->maybeMethodName($method)}(...$args);
});
}
protected function maybeMethodName($method)
{
return lcfirst(str_replace('scopeMaybe', '', $method));
}
public function scopeMaybeWhere($query, $predicate, ...$args)
{
return $this->maybeScope($query, $predicate, __FUNCTION__, $args);
}
public function scopeMaybeOrWhere($query, $predicate, ...$args)
{
return $this->maybeScope($query, $predicate, __FUNCTION__, $args);
}
public function scopeMaybeWhereIn($query, $predicate, ...$args)
{
return $this->maybeScope($query, $predicate, __FUNCTION__, $args);
}
public function scopeMaybeOrWhereIn($query, $predicate, ...$args)
{
return $this->maybeScope($query, $predicate, __FUNCTION__, $args);
}
public function scopeMaybeWhereNotIn($query, $predicate, ...$args)
{
return $this->maybeScope($query, $predicate, __FUNCTION__, $args);
}
public function scopeMaybeOrWhereNotIn($query, $predicate, ...$args)
{
return $this->maybeScope($query, $predicate, __FUNCTION__, $args);
}
public function scopeMaybeWhereExists($query, $predicate, ...$args)
{
return $this->maybeScope($query, $predicate, __FUNCTION__, $args);
}
public function scopeMaybeWhereNotExists($query, $predicate, ...$args)
{
return $this->maybeScope($query, $predicate, __FUNCTION__, $args);
}
public function scopeMaybeOrWhereExists($query, $predicate, ...$args)
{
return $this->maybeScope($query, $predicate, __FUNCTION__, $args);
}
public function scopeMaybeOrWhereNotExists($query, $predicate, ...$args)
{
return $this->maybeScope($query, $predicate, __FUNCTION__, $args);
}
public function scopeMaybeWhereBetween($query, $predicate, ...$args)
{
return $this->maybeScope($query, $predicate, __FUNCTION__, $args);
}
public function scopeMaybeWhereNotBetween($query, $predicate, ...$args)
{
return $this->maybeScope($query, $predicate, __FUNCTION__, $args);
}
public function scopeMaybeOrWhereBetween($query, $predicate, ...$args)
{
return $this->maybeScope($query, $predicate, __FUNCTION__, $args);
}
public function scopeMaybeOrWhereNotBetween($query, $predicate, ...$args)
{
return $this->maybeScope($query, $predicate, __FUNCTION__, $args);
}
public function scopeMaybeWhereNull($query, $predicate, ...$args)
{
return $this->maybeScope($query, $predicate, __FUNCTION__, $args);
}
public function scopeMaybeWhereNotNull($query, $predicate, ...$args)
{
return $this->maybeScope($query, $predicate, __FUNCTION__, $args);
}
public function scopeMaybeOrWhereNull($query, $predicate, ...$args)
{
return $this->maybeScope($query, $predicate, __FUNCTION__, $args);
}
public function scopeMaybeOrWhereNotNull($query, $predicate, ...$args)
{
return $this->maybeScope($query, $predicate, __FUNCTION__, $args);
}
public function scopeMaybeWhereColumn($query, $predicate, ...$args)
{
return $this->maybeScope($query, $predicate, __FUNCTION__, $args);
}
public function scopeMaybeOrWhereColumn($query, $predicate, ...$args)
{
return $this->maybeScope($query, $predicate, __FUNCTION__, $args);
}
public function scopeMaybeWhereDate($query, $predicate, ...$args)
{
return $this->maybeScope($query, $predicate, __FUNCTION__, $args);
}
public function scopeMaybeWhereTime($query, $predicate, ...$args)
{
return $this->maybeScope($query, $predicate, __FUNCTION__, $args);
}
public function scopeMaybeOrWhereDate($query, $predicate, ...$args)
{
return $this->maybeScope($query, $predicate, __FUNCTION__, $args);
}
public function scopeMaybeOrWhereTime($query, $predicate, ...$args)
{
return $this->maybeScope($query, $predicate, __FUNCTION__, $args);
}
public function scopeMaybeWhereDay($query, $predicate, ...$args)
{
return $this->maybeScope($query, $predicate, __FUNCTION__, $args);
}
public function scopeMaybeWhereMonth($query, $predicate, ...$args)
{
return $this->maybeScope($query, $predicate, __FUNCTION__, $args);
}
public function scopeMaybeWhereYear($query, $predicate, ...$args)
{
return $this->maybeScope($query, $predicate, __FUNCTION__, $args);
}
}
@davidjeddy
Copy link

Interesting. Couple observations:

  1. Nearly all method names are prepended with scope, why? Using the traits methods the user know's it is a scope centric method.
    Suggestion: Remove scope from method names.
  2. Many of the methods are simply interface's for scopeMaybeWhere...() in the naming. This is opinion but the repeated naming verbiage makes it a bit difficult to read.
  3. Define andMaybeWhere that is a facade for maybeWhere as many AR/ORM libraries allow chaining of the where clauses.
    ...also, and more importantly....
  4. Where's the tests? :P

@FatBoyXPC
Copy link
Author

FatBoyXPC commented Mar 1, 2017

Thanks for looking! Interestingly enough, most of these points are answered just by understanding how Laravel does query scopes.

  1. When you create a query scope in laravel, you prepend the method name with scope.
  2. All of the methods I defined here are the (what I consider) relevant where() methods that exist on a Query Builder object. I ignored the where methods that were for sub-selects as I feel like that is a different context. I can't imagine people often want to use a sub-select on a model, and query scopes only apply to models.
  3. All of the where methods default to and, and all of the orWhere methods just call the equivalent where method with the $boolean param set to or. This is just how Laravel set's it up! The same idea applies for the (or)whereNot methods. All of these query scope methods are chainable!
  4. No tests here because this was just to show the concept and the implementation :P But this is a very valid point!

@davidjeddy
Copy link

  1. Point taken. Sadly not that familiar with Laravel's query engine :(.
  2. Well stated.
  3. :/ Wish Yii did this...
  4. ;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment