Search With Filters from Other Tables - laravel

I try to build a search query with different params but some of the filters columns comes from other tables who are in relation with another table , i don't know how to achieve that
For exemple the column "compet_id" comes from my table Rencontre.
In my table RencontreOfficiel i have rencontre_id to make the relation with "Rencontre"
I'm not sure if my clear it's a little bit difficult to explain ; hope someone could see and help .
here my controller :
$query = RencontreOfficiel::query();
$filters = [
'compet_id' => 'compet_id',
'structure_id' => 'structure_id',
'catg_compet_id' => 'dt_rencontre',
'fonction_id' => 'dt_rencontre',
'bareme_id' => 'bareme_id',
'dt_min_rencontre' => 'dt_rencontre',
'dt_max_rencontre' => 'dt_rencontre',
];
$dt_min = $request->input('dt_rencontre_min');
$dt_max = $request->input('dt_rencontre_max');
foreach ($filters as $key => $column) {
$query->when($request->{$key}, function ($query, $value) use ($column , $dt_min , $dt_max) {
$query->where($column, $value)->orWhereBetween('dt_rencontre' , [$dt_min , $dt_max]);
});
}

You can use whereHas and with keyword in Eloquent search.
for example you have Blog method that connected to User model. every Blog send by user . If you want to have search in User according to his blogs you can use this :
$user = User::where(function($query){
$query->where('age','>=',18);
})->whereHas('blogs', function ($query){
$query->where('text', 'LIKE', '%game%');
});
In this code blogs in whereHas method is name of Eloquent method in User model.
Result : this code return users older than 18 years that have blogs with title like game.

Related

Laravel 5 with eloquent relation callback function returning wrong records

Here is User Model
public function userpackages()
{
return $this->hasMany('App\UserPackages');
}
Trying to get users packages form specific month and year but it returning all records.
$users = User::with(['team', 'userpackages' => function($package) use($month,$year) {
$package->whereMonth('created_at', $month)->whereYear('created_at', $year);
}])->get();
Fetching
foreach ($users as $key => $user) {
$userpackages = $user->userpackages;
}
If I'm understanding correctly, you are filtering the eager load, but this does not affect the models returned. You need to repeat the filter using whereHas() to limit the models that are returned. In addition, functions like whereDate() can be very inefficient, especially if the column is indexed. I suggest using whereBetween() when searching a date range.
$date = Carbon::createFromFormat("Y-m", "$year-$month");
$range = [$date->startOfMonth(), $date->endOfMonth()];
$users = User::with('team')
->with(['userpackages' => fn ($q) => $q->whereBetween('created_at', $range)])
->whereHas('userpackages', fn ($q) => $q->whereBetween('created_at', $range)
->get();
To explain further:
User::with('userpackages') returns all users, each with all of their packages.
User::with(['userpackages' => 'some condition']) returns all users, each with some of their packages
User::whereHas('userpackages', 'some condition') returns some users, each with all of their packages
User::(['userpackages' => 'some condition'])->whereHas('userpackages', 'some condition') returns some users, each with some of their packages.

Laravel Eloquent Get grouped by Relation Data

I am trying to get data with eager loading grouped by relation. Means, I want to eager load the relation in group by of a column. I have tried this code,
$customer = Customer::with(['orders' => function($query) {
$query->groupBy('shop_id');
}])->where('id', $id)->get();
Here the DB relation is,
Customer Has Many Orders (One to Many)
As an example, if I want to groupBy shop_id, I am expecting data in below format:
customer1 =>
otherProperties,
orders =>[
shop_id1 => [
order1,
order2 ...
],
shop_id2 => [
order5
]
]
But I am getting normal eager loaded data, like,
customer1 =>
otherProperties,
orders => [
order1,
order2,
order5,
]
Can anyone help me in this regard?
I can achieve similar result using raw query or php. But how I can achieve this using eloquent?
The groupBy happen on the collection and not on the query itself.
First suggestion : Create a custom scope
You could create a custom scope into your model with some Raw expressions : https://laravel.com/docs/5.8/queries#raw-expressions . Then, you will be able to do this:
$customer = Customer::with('newScope')->find($id);
Did you know that $customer = Customer::where('id', $id)->get()
return a collection of model and $customer = Customer::find($id)
return a single model?
$customer = Customer::where('id', $id)->get();
$customer = { [0] => {data} };
$customer = Customer::find($id);
$customer = { data };
Second suggestion : Use groupBy on the collection after the query
$customer = Customer::with('orders')->find($id);
$customer->orders = $customer->orders->groupBy('shop_id'));

Create a factory that doesn't return anything - Laravel

Intro
Hello everyone,
Recently I've picked Laravel and I'm still learning about the framework (which by the way I find amazing).
I'm working on a project in where i have a model called Order which I use for grouping other Order models (for example ClassicOrder, InstantOrder etc...) by using a one-to-one morph relationship.
The Orders table store an id, an order_id and the order_type which is used for the morph relationship.
The Problem
I've made a factory for each Order type and now I want to create a factory that generates n orders by randomly picking between all the order types.
I've done it like this:
$factory->define(Order::class, function (Faker $faker) {
$className = collect(Order::getModels())->random();
$order = factory($className)->create();
return [
'order_id' => $order->id,
'type' => get_class($order)
];
});
Now, this is working but the problem is that each order use a trait called Order which already register the order in the orders table so when I call the factory I'll get two rows in the order table for the same order.
This is the order trait:
Trait Order {
public static function boot()
{
parent::boot();
self::created(function ($model) {
// Add the order to the orders table to give him a public id
DB::table('orders')->insert(['order_id' => $model->id, 'type' => self::class]);
// Set and create the order path if the order isn't instant
if (!is_a($model, 'App\InstantOrder')) {
$orderType = explode('\\', get_class($model))[1]; // App\OrderType -> OrderType
$folderName = $orderType . '_' . $model->publicId . '_' . time() . '/';
$model->path = public_path() . '/storage/orders/' . $folderName;
$model->save();
File::makeDirectory($model->path, 0777, true);
}
});
self::creating(function ($model) {
$model->{$model->getKeyName()} = Uuid::generate()->string;
});
}
}
I can avoid this by calling factory()->make() instead of factory->create() but this doesn't seem right to me.
The Question
I've thought about some solutions and I've come out with the followings:
- Don't make the factory return anything, but looks like I can't.
- Delete the inserted rows before returning the data to store in the Orders table, and even if not really great, it looks like the only solution.
Can I make a factory without returning anything?
Thanks and wish a great day to everyone.
-Riccardo
Well lemme first welcome you, and then ask who said it wasn't a a good idea to make a factory that return anything, as it's mentioned in Laravel docs that's how it's written:-
use Illuminate\Support\Str;
use Faker\Generator as Faker;
$factory->define(App\User::class, function (Faker $faker) {
return [
'name' => $faker->name,
'email' => $faker->unique()->safeEmail,
'email_verified_at' => now(),
'password' => '$2y$10$TKh8H1.PfQx37YgCzwiKb.KjNyWgaHb9cbcoQgdIVFlYg7B77UdFm', // secret
'remember_token' => Str::random(10),
];
});
As mentioned Here
And then you can use it in Model factories as mentioned Here
I guess that's best practice as the Documentation says i guess.
And this is also a quick intro that you should check out for seeding data with Relationships

Laravel eloquent using loop

I am using eloquent query to filter data. My query is like this and it is working perfectly for now.
$users = User::where('username','LIKE','%'.request('username').'%')
->where('first_name','LIKE','%'.request('first_name').'%')
->where('last_name','LIKE','%'.request('last_name').'%')
->where('gender','LIKE','%'.request('gender').'%')
->where('password','LIKE','%'.request('password').'%')
->SimplePaginate(15);
My request data was like this.
However I need to update this query for dynamic fields. There can be different fields. What I did was to send the request in an associative array. So, my request data turned into like this,
What I intend to do is to put all request data in a search array. Then use a loop to run like the above query. How can I do that?
P.S. I checked in the forum and found some similar questions. Some of them are outdated. Others didn't solve my problem.
If I understand you right you can do like this:
$search = request('search', []);
$users = User::query();
foreach($search as $field=>$value)
{
$users = $users->where($field,'LIKE','%'.$value.'%');
}
$users = $users->SimplePaginate(15);
You can try this.
$search = $request->get('search');
User::where(function ($q) use ($search){
foreach ($search as $key=>$value) {
if($value != null ){
$q->where($key, 'like', "%{$value}%");
}
}
})->get();
The where() clause can also accept an array. With that in mind, you could map over your search parameters and convert it to the format you want and then feed it into the query.
For example:
$search = [
'first_name' => 'John',
'last_name' => 'Smith'
];
$filters = collect($search)->map(function($value, $key) {
return [$key, 'LIKE', "%{$value}%"];
})->values()->toArray();
return User::where($filters)->SimplePaginate(15);

Order by count of column in laravel 5.5 using eloquent orm

I am trying to set order by clause on count of field inside of larvel model.
I am using mongodb with Eloquent.
Is it even possible?
Here is a quick snippet of the query I am trying to run:
$books = Books::where(function ($query) use ($params, $res) {
$query->where('labels', 'elemMatch', array(
'genre' => $res->genre,
'outdated' => $res->false
))
->where('available', true)
->where('isSelling', true);
})
->orderBy('reviews','desc')
->paginate($params['limit']);
Reviews is an array in my database
Is it possible to order by the count of it?
you can try doing this
$books = Books::where(function ($query) use ($params, $res) {
$query->where('labels', 'elemMatch', array(
'genre' => $res->genre,
'outdated' => $res->false
))
->where('available', true)
->where('isSelling', true);
})
->orderBy('reviews','desc')
->get();
// sorting the books using sortBy or sortByDesc
$books = $books->sortByDesc(function($book){
return count(json_decode($book->reviews)); // we return the count of the reviews here
}
// get sorted ids of the books
$ids = $books->pluck('id')->toArray();
// we get the books paginated with the ids order
$books = $books->whereIn('id',$ids)
->orderByRaw(\DB::raw("FIELD(id, $ids)"))
->paginate($params['limit']);

Resources