How can I create a condition using current query with laravel query builder, is it even possible, i guess it would look something like this
$record = $this->repository
->where('value', $value)
->when($thisQuery->get()->isNotEmpty(), static function(){
//execute
});
You can use sub-queries in anonymous function as in the example below:
$record = $this->repository
->where('value', $value)
->where(function($query) {
/** #var $query Illuminate\Database\Query\Builder */
return $query->where('foo', 'LIKE', '%foooo%')
->orWhere('foo', 'bar');
})
->get();
You can as well run multiple where clauses as in the following:
$record = $this->repository
->where('value', $value)
->where('foo', 'bar')
->get();
And depends on the requirements use the appropriate way.
Related
I am trying to get users which have addresses and the time of creation of that address should not be more than 7 days:
$users = User::whereHas('address', function($q) {
$q->where(function ($query) {
$query->get()->filter(function ($address) {
return Carbon::now() < Carbon::parse($address->getOriginal('created_at'))->addDays(7);
});
});
});
the filter() works correct but I can't return its results, my result is all users who have addresses.
You can use whereDate like this:
$users = User::whereHas('address', function ($q) {
$q->whereDate('created_at', '>', Carbon::now()->subDays(7));
})->get();
you can try like this
$user = User::where('address',date('Y-m-d', strtotime('-7 days')))->where('created_at',date('Y-m-d', strtotime('-7 days')))->get();
Change your code with comments
$users = User::whereHas('address', function ($q): void {
$q->where(function ($query): void {
/**
* Subqueries use Eloquent Builder class here to add some wrapped filters
* But your code takes all addresses, filter them for nothing
* You should add SQL filters to builder something like that:
**/
$query->whereDate(Carbon::now()->subDays(7), '<', 'created_at');
})
/** To get users by your filters */
->get();
I have these 2 linked models: jobs and job_translations. A job have many translations. So in my job model, there is :
/**
* Get the translations for the job.
*/
public function translations()
{
return $this->hasMany('App\Models\JobTranslation');
}
In my controller, I want to build a query dynamically like that :
$query = Job::query();
if ($request->has('translation')) {
$query->translations()->where('external_translation', 'ilike', '%'.$request->translation.'%');
}
$jobs = $query->paginate(10);
I have this error :
Call to undefined method Illuminate\Database\Eloquent\Builder::translations()
Is it possible to do such a dynamic query with Eloquent?
Yes, it is possible. What you are looking for is whereHas('translations', $callback) instead of translations():
$query = Job::query();
if ($request->has('translation')) {
$query->whereHas('translations', function ($query) use ($request) {
$query->where('external_translation', 'ilike', '%'.$request->translation.'%');
});
}
$jobs = $query->paginate(10);
Your query can be improved further by using when($condition, $callback) instead of an if:
$jobs = Job::query()
->when($request->translation, function ($query, $translation) {
$query->whereHas('translations', function ($query) use ($translation) {
$query->where('external_translation', 'ilike', "%{$translation}%");
});
})
->paginate(10);
the issue you should do eager loading to detected on next chained query like this:
$query = Job::with('translations')->query();
I want to create dynamic filters.
for example I want to create this code
$Contact = Contact::whereHas('User', function ($query) use ($searchString) {
$query->where('name', 'like', '%Jhone%')->orwhere('family', '<>' . 'Doe');
})->whereHas('Account', function ($query) use ($searchString) {
$query->where('account_name', '=' , 'test_account' )->orwhere('account_city', 'like', '%test_city%');
})->get();
and all of parameters is variable
name,like,%Jhone%,family,<>,Doe,.....
and I want to pass variables to function and function create above query.
I assume that the relationship functions within your Contact, User and Account models are written in camelCase and not PascalCase like your example shows.
public function getContacts(Request $request)
{
return Contact::when($request->get('username'), function ($query, $val) use ($request) {
$query->whereHas('user', function ($q) use ($val, $request) {
$q->where('name', 'like', '%'.$val.'%');
if ($request->has('familyname')) {
$q->orWhere('family', '<>', $request->get('familyname'));
}
});
})
->when($request->get('accountname'), function ($query, $val) use ($request) {
$query->whereHas('account', function ($q) use ($val, $request) {
$q->where('account_name', $val);
if ($request->has('city')) {
$q->orWhere('account_city', 'like', '%'.$request->get('city').'%');
}
});
})
->get();
}
This function will return all contacts when no GET parameters are given on the request. If a parameter for username is present, it will only return contacts where a user with the given name exists for. If furthermore a familyname parameter is present, it will give contacts with a user that has a matching username or a familyname different from the one given. The very same applies to the account, accountname and city.
In particular, there are two things interesting about this example:
The when($value, $callback) function can be used to build very dynamic queries which only execute the $callback when $value is true. If you use $request->get('something') and something is not available as parameter, the function will return null and the callback is not executed. The callback itself has the form function ($query, $value) { ... }, where $value is the variable you passed to when() as first parameter.
Using $request->has('something') inside the query builder functions to dynamically build constraints on the query is an alternative to when(). I only added it for the purpose of demonstration - in general I'd recomment sticking to one style.
If you would extend on the example, you could also build highly dynamic queries where not only the variable content like Doe for the family name is given as parameters, but also the comparator like =, <> or like. But extending further on this topic is too much for this answer and there are already tutorials about this topic available anyway.
Edit: here an example for a dynamic query with more detailed input
Expected input (slightly different than your request because yours cannot work):
$filters = [
'user' => [
['name','like','%Jhone%'],
['family','<>','Doe'],
],
'account' => [
['account_name','=','test_account'],
['account_city','like','%test_city%'],
]
];
And the function:
public function getContacts(Request $request, array $filters)
{
$query = Contact::query();
foreach ($filters as $key => $constraints) {
$query->whereHas($key, function ($q) use ($constraints) {
if (count($constraints) > 0) {
$q->where($constraints[0][0], $constraints[0][1], $constraints[0][2]);
}
for ($i = 1; $i < count($constraints); $i++) {
$q->orWhere($constraints[$i][0], $constraints[$i][1], $constraints[$i][2]);
}
});
}
return $query->get();
}
This will always use OR for multiple constraints and not AND. Using AND and OR mixed would require a lot more sophisticated system.
I have an Eloquent\Builder $query that I want to use additional where() calls on, where the amount of the calls is indefinite and is taken from an array $filter, example below:
$filter = [
'or:email:=:ivantalanov#tfwno.gf',
[
'or:api_token:=:abcdefghijklmnopqrstuvwxyz',
'and:login:!=:administrator',
],
];
The strings, when parsed, produce valid SQL conditions, but the problem lies into sticking them into closures where a group is present (like strings 2 and 3 in the example - the array is their 'group').
I know of Laravel's functionality that allows sticking a Closure function into $query->where() to achieve what I want, but the problem I'm facing is actually building those complex closures. I have to iterate through every string in the group and pass it into the closure generated like so (where $item is the result of parsing a condition string):
$closure = function ($query) use ($item)
{
call_user_func_array(
[$query, $item['function']], [$item['field'], $item['operator'], $item['values']]
);
};
Now the obvious problem with this is while it makes simple closures easily, passing more than one condition is plain impossible.
My question is, what could I use to prepare a complex statement to be executed on a query inside a closure?
Okay, I think I figured it out.
This is the method that will return the end result.
public function parse_filter(Builder &$query, array $filter)
{
$groups = $this->_prepare_groups($filter);
return $this->_parse_groups($query, $groups);
}
These methods will parse the initial array into something more usable.
private function _prepare_groups(array $filter)
{
foreach ($filter as $key => $item) {
if (is_array($item)) {
$groups[] = $this->_prepare_groups($item);
}
if (is_string($item)) {
$simple_filter = $this->_parse_simple_filter($item);
$groups[] = $simple_filter;
$simple_filter = null;
}
}
return $groups;
}
private function _parse_simple_filter(string $filter)
{
$filter_data = explode(':', $filter);
$simple_filter['function'] = $filter_data[0] === 'and' ? 'where' : 'orWhere';
$simple_filter['field'] = $filter_data[1];
$simple_filter['operator'] = $filter_data[2];
$simple_filter['values'] = $filter_data[3];
return $simple_filter;
}
And here is where the most of the magic happens. Closures are recursive calls to this method, as you can see.
private function _parse_groups(Builder &$query, array $groups)
{
foreach ($groups as $operator => $group) {
if (!array_key_exists('function', $group)) {
$closure = function ($query) use ($group)
{
$this->_parse_groups($query, $group);
};
$query->where($closure);
} else {
$query->{$group['function']}($group['field'], $group['operator'], $group['values']);
}
}
return $query;
}
Using this, you can modify an Eloquent\Builder object however you like with deeply nested filters that are declared dynamically (for example, received within a GET/POST request).
I guess this will help for you:
In model create a scope:
public static function scopeGetResultList($query) {
return $query->where(function ($query) use ($item) {
$query->where('group_user_holder_type', '=', 1)
->orWhere('group_user_holder_type', '=', 0);
});
}
OR
Example:
public static function getSearchedUserAuto($search_key, $user_id)
{
$users = DB::table((new User)->getTable().' as U')
->select('U.*', 'CT.city_name', 'C.nicename')
->leftJoin((new Country)->getTable().' as C', 'C.country_id', '=', 'U.user_country')
->leftJoin((new City)->getTable().' as CT', 'CT.city_id', '=', 'U.user_city')
->where(function($query) use ($search_key){
$query->where('U.user_full_name', 'like', '%'.$search_key.'%')
->orWhere('U.user_email', 'like', '%'.$search_key.'%');
})
->where(function($query) use ($search_key){
$query->where('U.user_full_name', 'like', '%'.$search_key.'%')
->orWhere('U.user_email', 'like', '%'.$search_key.'%');
})
->where('U.status', '=', 1)
->where('U.user_id', '!=', $user_id)
->get();
return $users;
}
See if it is work for you.
Hello everyone I'm trying to make pagination in Laravel 4 but my code doesn't work.
I have controller with action:
public function getSingleProduct($prodName, $id)
{
$singleProduct = Product::getOne($id);
$getAllReviews = Review::getAllBelongsToProduct($id);
$this->layout->content = View::make('products.single')
->with('reviews', $getAllReviews)
->with('products', $singleProduct);
}
and I want to paginate getAllReviews (5 per page). I tried like this:
$getAllReviews = Review::getAllBelongsToProduct($id)->paginate(5); but it doesn't work for me. Here is also my Review model
public static function getAllBelongsToProduct($id) {
return self::where('product_id', '=', $id)
->join('products', 'reviews.product_id', '=', 'products.id')
->select('reviews.*', 'products.photo')
->orderBy('created_at', 'desc')
->get();
}
Where I have a mistake?
Instead of that static method on your model use query scope, this will be flexible:
// Review model
public function scopeForProduct($query, $id)
{
$query->where('product_id', $id);
}
public function scopeWithProductPhoto($query)
{
$query->join('products', 'reviews.product_id', '=', 'products.id')
->select('reviews.*', 'products.photo');
}
Then use it:
// get all
$reviews = Review::forProduct($id)->withProductPhoto()->latest()->get();
// get paginated
$reviews = Review::forProduct($id)->withProductPhoto()->latest()->paginate(5);
latest is built-in method for orderBy('created_at', 'desc').
If you want to have just a single call in your controller, then chain the above and wrap it in methods on your model.