laravel query builder, optional search parameters - laravel

Laravel newbie looking for guidance. Can anyone please show me the best way to add optional posted search parameters to the following Eloquent query and return all related data and paginated? The optional search parameters are the foreign keys; country_id, season_id and parameter_id.
$dataitems = Dataitem::orderBy('name')->with('country')->with('season')->with('parameter')->paginate(10);
I am reading the docs and have seen the fluent query builder I'm just struggling to recreate this nicely paginated query.
Many thanks.

I am not sure if I understand what you want. But i would do something like this :
Create your base eloquent query :
$query = Dataitem::orderBy('name');
If you have a specific parameter, add the condition to you existant query.
You can repeat this if block for all your optional parameters.
if (Input::has('country_id'))
{
$query->whereHas('country', function($q){
$q->where('id', '=' ,Input::get('country_id'));
});
}
When you're done, paginate:
$query->paginate(10);

Related

Laravel Scout - Where not equal to

I'm building a query for Laravel Scout.
$subchanResults = Subchan::search($query)
->when($request->input('incl_nsfw'), function ($query, $incl_nsfw) {
if ($incl_nsfw == 0) {
return $query->where('nsfw','!=', 'always');
}
}, function ($query) {
return $query->where('nsfw','!=', 'always');
})
->paginate(3);
Laravel Scout only allows basic where clauses, no advanced conditional clauses of any kind, so it's been kind of tricky so far. (Documentation: https://laravel.com/docs/8.x/scout#where-clauses)
My problem is with this line: return $query->where('nsfw','!=', 'always');
It seems that Scout will allow a simple where('column','value') , but if I try to add the 3rd parameter to the where clauses (not equal), it doesn't work.
How can I make this query work? Or do I have to manually trim the results myself after the query?
The docs you linked to explain:
Scout allows you to add simple "where" clauses to your search queries. Currently, these clauses only support basic numeric equality checks and are primarily useful for scoping search queries by an owner ID. Since a search index is not a relational database, more advanced "where" clauses are not currently supported
Basically, when you call ::search you are no longer building an Eloquent ORM query; you’re building a Scout query, and the where method for a Scout query only accepts two parameters: a key and a value to match against:
https://github.com/laravel/scout/blob/7a8d5b90761e0c102d357dd9e57d8c2f726ceaa5/src/Builder.php#L103-L110
I was hoping to find a workaround within Scout itself, but it seems that conditional clauses are just completely out of the question at the moment for scout. Here is my solution for filtering at the search engine level (Meilisearch) for the same scenario:
$subchanResults = Subchan::search($query, function ($meilisearch, $query, $options) use ($request) {
if ($incl_nsfw = $request->input('incl_nsfw')) {
$options['filters'] = 'nsfw != always';
}
return $meilisearch->search($query, $options);
})->get();

How to know what columns are presents in a Eloquent query before to execute it in Laravel 5.5?

Im using Laravel 5.5 and I have and QueryBuilder object (from the "Illuminate/Database/Eloquent/Builder" class).
I want to set an orderBy sentence into my query, but only if this field is present and exists in the QueryBuilder object (as column in the select section sentence).
For example, there is an User model, with the following fields ['id', 'firtsname', 'lastname', 'username','description'].
This is my object:
Use App\User;
$query = User::query();
if ($request->input('sort') != null) {
$model_query->orderBy($request->input('sort'), 'ASC');
}
$users = $query->get();
When I execute it, works fine (if I send you consistent data, of course). But if I set a column what does not exists, it sends and exception. So, the question is, how I can get the columns to retrieve from my $query object? To validate it, and if it's presents, execute the ordening code.
To answer your question, you can get the presence status of a column using Schema::hasColumn()
if (Schema::hasColumn('users', $request->sort)) {
//
}
GOING FURTHER
Now this doesn't seem very efficient, and maybe potentially leak data. Better validating your sort input and accept only proper column names:
$request->validate(['sort' => 'in:column1,column2']);

Why loadCount() introduced and its actual usage into laravel

I already read doc here :
https://github.com/laravel/framework/pull/25997
What i want to know is by using withCount() we were just load count of records instead of getting all relations data.
So by using loadCount() what we can do ?
Please explain in short in simple words.
Thanks
loadCount Eloquent Collection Method introduced by the release of Laravel 5.7.10. According to the laravel-news.
loadCount is the ability to load relationship counts on an Eloquent collection. Before this feature, you could only load relationships, but now you can call loadCount() to get counts for all relations.
The pull request illustrates how you could use loadCount() with the following example:
$events = Event::latest()->with('eventable')->paginate();
$groups = $events->map(function ($event) {
return $event->eventable;
})->groupBy(function ($eventable) {
return get_class($eventable);
});
$groups[Post::class]->loadCount('comments');
$groups[Comment::class]->loadCount('hearts');
return new EventIndexResponse($events);

Laravel: How to retrieve this nested model

In Laravel I have ModelA, ModelB and ModelC. ModelA has many ModelB. ModelB has many ModelC. I want to retrieve all ModelC for a selection of ModelA. How do I do this?
I tried the following:
$models = ModelC::with(['modelB','modelB.modelA' => function ($query) {
$query->where('owner', 123);
}])->get();
But the first query in that case is select * from model_c. Obviously not the result I am looking for.
Imagine that you were received 100 objects from the database, and each record had 1 associated model (i.e. belongsTo). Using an ORM would produce 101 queries by default; one query for the original 100 records, and additional query for each record if you accessed the related data on the model object. In pseudo code, let’s say you wanted to list all published authors that have contributed a post. From a collection of posts (each post having one author) you could get a list of author names like so:
$posts = Post::published()->get(); // one query
$authors = array_map(function($post) {
// Produces a query on the author model
return $post->author->name;
}, $posts);
We are not telling the model that we need all the authors, so an individual query happens each time we get the author’s name from the individual Post model instances.
Eager Loading
As I mentioned, ORMs “lazy” load associations. If you intend to use the associated model data you can trim that 101 query total to 2 queries using eager loading. You just need to tell the model what you need it to load eagerly.
Here’s an example from the Rails Active Record guide on using eager loading. As you can see, the concept is quite similar to Laravel’s eager loading concept.
$posts = Post::with('author')->limit(100)->get();
I find that I receive better understanding by exploring ideas from a wider perspective. The Active Record documentation covers some examples that can further help the idea resonate.
I managed to solve this with nested whereHas calls as follows:
$models = modelC::whereHas('modelB', function ($query) {
$query->whereHas('modelA', function ($query) {
$query->where('owner', 123);
});
})->get();
Laravel to the rescue, yet again!

Use Laravel 5.3 Query Builder to replicate Eloquent ORM data structure for Sub Query

I am trying to replicate the result set that I get when using Eloquent ORM, except with Laravel Query Builder. Basically using this code I can get the packs to appear nested within the products so that when I loop them on the view I can further loop the packs within each products. Seems pretty basic right (see result set below).
$get_posts_for_product = Product::where('active', 1)
->with('packs')
->get()->toArray();
I have tried a few ways using Query Builder to get this to work but it joins the packs inline as I thought it would.
What is the best way to get this same Array structure using Query Builder, I am aware that the result set is a different type of array and that is fine but for my project it must be done using Query Builder at this point.
Thanks.
I would say, that is why you have Eloquent: you don't have to worry about how to have those relationships together.
However incase you really want to achieve the same result I will demo this using two tables users and messages:
1st method:
Retrieve the users and transform it by querying the database for relationships:
$result = DB::table('users')->get()->transform(function ($user){
$user->messages = DB::table('messages')->where('user_id', $user->id)->get();
return $user;
});
Downside: Having many users means a lot of db query on messages table.
Upside: less codes to write
2nd method:
Retrieve both tables using all the ids of user to query the messages:
$users = DB::table('users')->get();
$messages = DB::table('messages')->whereIn('user_id', $users->pluck('id')->toArray())->get();
$result = $users->transform(function ($user) use ($messages){
$user->messages = $messages->where('user_id', $user->id)->values();
return $user;
});
Downside: The need to still transform it.
Upside: Less database trips. i.e two queries only.
3rd method
Looks like the second except that you can group messages by 'user_id' then you do no extra filter when transforming users result:
$user = DB::table('users')->get();
$messages = DB::table('messages')->whereIn('user_id', $user->pluck('id')->toArray())
->get()
->groupBy('user_id');
$result = $user->transform(function ($user) use ($messages){
$user->messages = $messages[$user->id];
return $user;
});
Downside: Same with two.
Upside: no extra filter when transforming users.
Other method
Join on both users and messages when querying then transform the response, or simply use it as it is.
PS: Eloquent uses query builder.
The answer is open for update.

Resources