Where not in with a belongs to relationship in laravel - laravel

I have two table named 'districts' & 'division' as follows:
division table
districts table
division model hasMany districts
Now I want to get all division with districts except some districts. I am trying as follows:
$divisionWithDistricts = Division::with('districts')->whereNotIn('districts.id',$districtsAlreadyUsed)->get();
Here districtsAlreadyUsed is an array of district ids. I got Unknown column 'districts.id' error. It was so easy if district_id was present at division table. How can I get such data at this situation?

Try this
$divisionWithDistricts = Division::with(['districts' => function($query) use($districtsAlreadyUsed) {
$query->whereNotIn('id', $districtsAlreadyUsed);
}])
->get();

Related

Laravel Eloquent: Getting sum of data in nested relations and adding it as a new attribute

I want to find a single query for a complex problem (and an even more complicated one). Hope someone can help me.
I've an orders table with many products (many to many relationship). You can order products and select a quantity, which is saved in the pivot table.
I know I can get all orders with their products with
$query = Order::with(['products'=>function($q){
$q->select('name', 'price', 'quantity', 'unit')->orderBy('name','asc');
}]);
Now what I want to retrieve is an array of objects containing the name of every product only once with the sum of their quantity for all orders.
In this case, the products represent types of meat. So the outcome should look something like this:
[{
name: steak,
unit: piece,
totalQuantity: 20
},
{
name: bacon,
unit: kg,
totalQuantity: 112
}
]
If someone could come up with a solution for this, that would be great!
Somewhat more complicated:
Every order can also have many colli's (packages) which contain many products with a certain amount/quantity.
So orders table -> manyToMany -> collis table -> manyToMany -> products table
I need the same as above, but now with the sum of all quantities of all products in the colli's together with the sum of all quantities of the other products.
Any help is appreciated because I can't find many in the docs about eager loading.
You can try this.
$query = Order::with('products'=>function($q){
$q->select('name', 'price','unit',DB::raw("SUM(quantity) as total_quantity"))->groupBy('name')->orderBy('name','asc');
});
i hope it would work for you.
Found the answer to the first problem:
$allProducts = DB::query()
->select(['p.id', 'p.name', 'c.name as category_name', DB::raw('sum(op.quantity) as total_product_quantity')])
->from('products as p')
->join('order_product as op', 'p.id', '=', 'op.product_id')
->join('orders as o', 'op.order_id', '=', 'o.id')
->join('categories as c', 'p.category_id', '=', 'c.id')
->groupBy('p.id');
For the second problem, I get exactly the same table as above. Now with all products in all colli's:
$allProductsInAllCollis = DB::query()
->select(['p.id', 'p.name', 'c.name as category_name', DB::raw('sum(co.quantity * colp.quantity) as total_colli_quantity')])
->from('products as p')
->join('colli_product as colp', 'p.id', '=', 'colp.product_id')
->join('collis as col', 'colp.colli_id', '=', 'col.id')
->join('colli_order as co', 'col.id', '=', 'co.colli_id')
->join('orders as o', 'co.order_id', '=', 'o.id')
->join('categories as c', 'col.category_id', '=', 'c.id')
->groupBy('p.id', 'p.name', 'category_name');
(No idea why I had to pass 'p.name' and 'category_name' in the groupBy, but got an sql error otherwise.
Now the last thing to do is perform another groupBy to add 'total_product_quantity' of the first table to 'total_colli_quantity' in the second table. How can I do this with 2 queries saved in variables?

Undefined relationship error in sorting of existence and non existence relationship using Eager Load in Laravel eloquent model

I have a Student model and a corresponding one to one mapping relationship to Result model.
I have an eligibleList array containing a list of student id whose marks are to be displayed. Some student have results while some does not have but i need to display all of them from the list.
I am able to retrieve and display students using the following:
$students = Student::with('result:student_id,marks')->whereIn('students.id', $eligibleList)->get();
foreach($students as student) {
if ($student->result != null)
Log::debug($student->result->marks)
else
Log::debug("-1") //-1 indicate no results
}
The above has no issue until i need to sort the list (ascending or descending) by the marks. I tried the following:
$students = Student::with(['result:student_id,marks' => function ($query) {
$query->orderBy('marks','DESC');
}])->whereIn('student.id', $eligibleList)->get();
It throws me a "Call to undefined relationship" error. Is there anyway to sort from the query ? I avoid sorting the collection as it can get very slow for thousands of records. Somehow eloquent early loading encounter some error when sorting with non existence relationship.
you should use 'Subquery Ordering', ordering inside 'with' will not sort the overall result.
$students = Student::with(['result:student_id,marks'])->whereIn('student.id', $eligibleList)
->orderByDesc(Result::select('marks')->whereColumn('student_id','students.id'))
->get();
https://laravel.com/docs/7.x/eloquent#advanced-subqueries
if you use laravel 5 you have to use 'join':
Student::with(['result:student_id,marks'])->whereIn('student.id', $eligibleList)
->join('result','result.student_id','student.id')
->select('user.*,result.marks')->orderBy('result.marks')->get();
'join' use table name not the relation name, so please be careful about table name in previous 'join' and 'select' statements

Laravel eloquent with relation data (Eager Loading)

I have two database tables items and measurement_units - item has measurement unit.
Now the problem is I want to select a particular column from items and some column from measurement_unit. I want to use Eager loading
e.g.
$items_with_mu = Item::with("measurement_unit")->select(["item_name", "item_stock"])->first();
When accessing measurement_unit. It returns null. Without the select function it returns data(measurement_unit).
$items_with_mu->measurement_unit;
can anyone help me and sorry for my English.
Try this
Item::with(['measurement_unit' => function($q) {
$q->select('id','unit_column'); //specified measurement_unit column
}])
->select('id','measurement_unit_id','item_name')
->get();
If your laravel version is >=5.5 then you can write in a single line
Item::with('measurement_unit:id,unit_column')
->select('id','measurement_unit_id','item_name')
->get()
You have to select the primary column of the main model like below.
items_with_mu = Item::with("measurement_unit")->select(["item_name", "item_stock", "primary_key"])->first();

Eloquent query: Retrieving data from two related models having Same column names

Following gives the list of ID's of SectionDetail Model while I need List of ID's of Section Model:
SectionDetail::with('section')->where('class_id', '=', Input::get('grade_id'))->lists('id');
Problem is both Models SectionDetail and Section has columns "ID".
How can I point to the ID of SectionDetail and Section Model in my Query
You can't do that this way, since there are 2 separate queries fetching SectionDetail and Section.
In order to get Section ids you need to query that model filtered by the relation constraint:
$gradeId = Input::get('grade_id');
// assuming sectionDetails is relation name on the Section model
$sectionsIds = Section::whereHas('sectionDetails', function ($q) use ($gradeId) {
$q->where('class_id', '=', $gradeId); // use prefixed column name in case it's ambiguous
})->lists('id');

Laravel Many to Many - 3 models

Some help with many to many relationships in Laravel:
Using the example for roles and users - basically:
a table for all the roles
a table for the users
and table with user_id and role_id.
I want to add to the third table, eg Year. basically the pivot table will have user_id, role_id and year_id.
I want to be able to make a query to pull for example all users assigned a specific role in a specific year. Eg All users with role_id = 2, and year_id = 1.
Any help will be appreciated
Before answering, I would like to suggest you not to put year on database like this.
All your tables should have created_at and updated_at which should be enough for that.
To filter users like you want. You could do this:
// This queries all users that were assigned to 'admin' role within 2013.
User::join('role_users', 'role_users.user_id', '=', 'users.id')
->join('roles', 'roles.id', '=', 'role_users.role_id')
->where('roles.name', '=', 'admin')
->where(DB::raw('YEAR(role_users.created_at)', '=', '2013')
->get();
This example may not be the precise query you are looking for, but should be enough for you to come up with it.
The best way to achieve a three way relation with Eloquent is to create a model for the table representing this relation. Pivot tables is meant to be used for two way relations.
You could have then a table called roles_users_year which could have data related to this 3 way relation like a timestamp or whatever...
A very late answer to a very old question, but Laravel has supported additional intermediate (pivot) table columns of at least Laravel 5.1 judging from the documentation, which hasn't changed at least through Laravel 6.x.
You can describe these extra columns when defining your many-to-many relationship:
return $this->belongsToMany(Role::class)->withPivot('column1', 'column2');
or in your case, the below would also do the job:
return $this->belongsToMany(Role::class)->withTimestamps();
which you can then access via the pivot attribute on your model:
$user = User::find(1);
foreach ($user->roles as $role) {
echo $role->pivot->created_at;
}
Note that the pivot attribute is on the distant relationship model (a single Role) and not on the relationship itself.
To get all the Roles assigned to Users in any given year, you might create a special relationship:
// User.php
public function rolesInYear($year) {
return $this->belongsToMany(Role::class)
->wherePivot('created_at', '>=', Carbon::create($year))
->wherePivot('created_at', '<', Carbon::create($year + 1));
}

Resources