Laravel - Sum of pivot table results given a criteria - laravel

Scenario
An alert can have many criterias, and many criterias can have my alerts.
A user is assigned to a criteria. When a user views all of their criteria, I want it to show the sum of how many matching alerts there are (stored in the pivot table). The relationship uses a pivot table structured as:
So for example, I can access the alert and criteria data, and it should also tell me that for criteria_id = 73, there is one alert match id_2.
So the Eloquent formula is currently:
public function getIndex()
{
$alerts = Criteria::with('coordinate', 'alerts')
->where('user_id', '=', Auth::user()->id)
->get();
$this->layout->content = View::make('users.alert.index',
array('alerts' => $alerts));
}
Relationship within Alert model
public function criterias()
{
return $this->belongsToMany('Criteria')->withTimestamps();
}
Relationship within Criteria model
public function alerts()
{
return $this->belongsToMany('Alert')->withTimestamps();
}
If you require any further information, please let me know. Many thanks for your help.

I solved my question by using #JarekTkaczyk's example above.
Getting count from pivot table in laravel eloquent

Related

Laravel eloquent for four tables

I'm new to Laravel. I am developing a project. and in this project I have 4 tables related to each other
-Users
-Orders
-OrderParcels
-Situations
When listing the parcels of an order, I want to get the information of that order only once, the user information of that order once again, and list the parcels as a table under it. so far everything ok. but I also want to display the status of the parcels listed in the table as names. I couldn't add the 4th table to the query. do you have a suggestion? I'm putting pictures that explain the structure below.
My current working code is
$orderParcels = Orders::whereId($id)
->with('parcels')
->with('users:id,name')
->first();
and my 'orders' model has method
public function parcels(){
return $this->hasMany(OrderParcels::class);
}
public function users(){
return $this->hasOne(User::class,'id','affixer_id');
}
Note[edit]: I already know how to connect like this
$orderParcels = DB::table('order_parcels as op')
->leftjoin('orders as o','op.orders_id','o.id')
->leftjoin('users as u','o.affixer_id','u.id')
->leftjoin('situations as s','op.status','s.id')
->select('op.*','o.*','u.name','s.situations_name')
->where('op.orders_id',$id)->get();
but this is not working for me, for each parcels record it returns me orders and user info. I want once orders info and once user info.
Laravel provides an elegant way to manage relations between models. In your situation, the first step is to create all relations described in your schema :
1. Model Order
class User extends Model {
public function parcels()
{
return $this->hasMany(OrderParcels::class);
}
public function users()
{
return $this->hasOne(User::class,'id','affixer_id');
}
}
2. Model Parcel
class Parcel extends Model {
public function situations()
{
return $this->hasOne(Situation::class, ...);
}
}
Then, you can retrieve all desired informations simply like this :
// Retrieve all users of an order
$users = $order->users; // You get a Collection of User instances
// Retrieve all parcels of an order
$parcels = $order->parcels; // You get a Collection of User instances
// Retrieve the situation for a parcel
$situations = $parcel->situations // You get Situation instance
How it works ?
When you add a relation on your model, you can retrieve the result of this relation by using the property with the same name of the method. Laravel will automatically provide you those properties ! (e.g: parcels() method in your Order Model will generate $order->parcels property.
To finish, in this situation where you have nested relations (as describe in your schema), you should use with() method of your model to eager load all the nested relation of order model like this :
$orders = Orders::with(['users', 'parcels', 'parcels.situations'])->find($id)
I encourage you to read those stubs of Laravel documentation :
Define model relations
Eager loading
Laravel Collection
Good luck !
Use join to make a perfect relations between tables.
$output = Orders::join('users', 'users.id', '=', 'orders.user_id')
->join('order_parcels', 'order_parcels.id', '=', 'orders.parcel_id')
->join('situations', 'situation.id', '=', 'order_parcels.situation_id')
->select([
'orders.id AS order_id',
'users.id AS user_id',
'order.parcels.id AS parcel_id',
'and so on'
])
->where('some row', '=', 'some row or variable')->get();

laravel eloquent with pivot and another table

I have 4 table categories, initiatives, a pivot table for the "Many To Many" relationship category_initiative and initiativegroup table related with initiatives table with initiatives.initiativesgroup_id with one to many relation.
With pure sql I retrive the information I need with:
SELECT categories.id, categories.description, initiatives.id, initiatives.description, initiativegroups.group
FROM categories
LEFT JOIN category_initiative ON categories.id = category_initiative.category_id
LEFT JOIN initiatives ON category_initiative.initiative_id = initiatives.id
LEFT JOIN initiativegroups ON initiatives.initiativegroup_id = initiativegroups.id
WHERE categories.id = '40'
How can I use eloquent model to achieve same results?
Since you have such a specific query touching multiple tables, one possibility is to use query builder. That would preserve the precision of the query, retrieving only the data you specifically need. That would look something like this:
$categories = DB::table('categories')
->select([
'categories.id',
'categories.description',
'initiatives.id',
'initiatives.description',
'initiativegroups.group',
])
->leftJoin('category_initiative', 'categories.id', '=', 'category_initiative.category_id')
->leftJoin('initiatives', 'category_initiative.initiative_id', '=', 'initiatives.id')
->leftJoin('initiativegroups', 'initiatives.initiativegroup_id', '=', 'initiativegroups.id')
->where('categories.id', '=', 40)
->get();
In your models define the relationships:
Category.php model
public function initiatives()
{
return $this->belongsToMany('App\Initiative');
}
Initiative.php model (If has many categories change to belongs to many)
public function category()
{
return $this->belongsTo('App\Category');
}
Then maybe change your initiativegroup -> groups table, and then create a pivot table called group_initiative. Create model for group. Group.php and define the relationship:
public function initiatives()
{
return $this->belongsToMany('App\Initiative');
}
Then you can also add the following relationship definition to the Initiative.php model
public function group()
{
return $this->belongsTo('App\Group');
}
That should get you started.
for the record..
with my original relationship, but changing table name as alex suggest, in my controller:
$inits = Category::with('initiative.group')->find($id_cat);
simple and clean

Retrieving eloquent models with constraints on both the parent and child/associated model

I'm trying to retrieve all Eloquent Models that match a particular field in the Parent Model ('Event') and the child model ('Dates').
I've hit an issue whereby Laravel is stating that my field ('date') doesn't exist in the child class, but I can't understand why. Can someone please point me in the right direction and explain where I've gone wrong?
Essentially, what I'm trying to achieve is the retrieval of all Events with the approved tag being true AND where the event date is of a particular day, in this case the 10th.
I've done some searching around and looked at some of the examples in the Laravel documentation. I've set up the ('Event') model to have a one to many relationship with the ('dates') model. I can see that I can chain queries together, but things get a little confusing when dealing with more than one model at a time (in the same query)
This is my attempt at retrieving the data.
public function calender()
{
$events = Event::where('approved', true)->with('EventDates')->whereDay('date', '10')->get();
return view('events.calender');
}
This is a snippet from my ('Event') Model. I've only included the most relevant information here as there are many attributes .
class Event extends Model
{
//
public function user(){
return $this->belongsTo(User::class);
}
public function dates()
{
return $this->hasMany('App\EventDate');
}
}
This is a snippet from my ('EventDate') model migration file showing that 'date' is indeed a field of the ('EventDate') model. Once again, I've just included the most relevant function here.
class CreateEventDatesTable extends Migration
{
public function up()
{
Schema::create('event_dates', function (Blueprint $table) {
$table->bigIncrements('id');
$table->timestamps();
$table->date('date')->nullable();
$table->time('startTime')->nullable();
$table->time('endTime')->nullable();
$table->unsignedBigInteger('event_id');
$table->index('event_id');
});
}
}
I'd like to be able to retrieve a list of the matching Events that have the approved attribute set to true, and the Event Dates on a particular day (xxxx-xx-10)
Right now, I'm getting the error that the date column can't be found:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'date' in 'where clause' (SQL: select * from events where day(date) = 10 and approved = 1)
I think this is what you are looking for:
$events = Event::where('approved', true)
->with(['dates' => function ($query) {
return $query->whereDay('date', '10');
}])
->get();
Note: I assume your relation between an Event and it's EventDate is called dates
In this way you are applying filtering by day on the related data (EventDate) and not on the Event model.
you should mention the table name for the date column.
->whereDay('event_dates.date', '10')
I managed to find a way around using the eloquent commands by using the DB Query builder instead and it now works (taking into account Shankar's point above)
I altered the code to this (It's a little more specific than what I wrote before, but it should be able to guide others to solving their issue):
for($day = 5; $day <12; $day++)
{
$events = DB::table('events')
->join('event_dates', 'events.id', '=', 'event_dates.event_id')
->select('events.*', 'event_dates.startTime as startTime', 'event_dates.endTime AS endTime')->where('events.approved', '=', true)->whereDay('event_dates.date', '=', $day)
->orderBy('event_dates.startTime', 'asc')->get();
array_push($events_list, $events);
}
```
I can't understand why the Eloquent queries couldn't find the table, but this seems to work, so for those stuck, this may be less "eloquent" but at least it works ;-;

Ordering data from a table with the help of other table by using relationships with laravel?

I have the controller with the query:
$Comments = Comment::orderBy('id_parent', 'asc')->get();
And I have the Comment model:
class comment extends Model
{
public function user()
{
return $this->belongsTo('App\User');
}
public function votes()
{
return $this->hasMany('App\Vote', 'comment_id', 'id_comment');
}
}
I want to retrieve the comments data sorted in a specific way, every comment has multiply votes voted by different users, so the count('vote') is the number of votes for each comment. The problem is that I am stuck with how to call the specific votes function in the model so that it can count the column vote and order it either asc or desc.
That in the end I can have the $Comments sorted also by the total number of votes.
You can try as:
$Comments = Comment::withCount('votes')->orderBy('votes_count', 'asc')->get();
withCount() method is used when you want to count the number of results from a relationship without actually loading them, which will place a {relation}_count column on your resulting models.
Using sortBy:
$comments=Comment::with('votes')->get()->sortBydesc('votes');
foreach($comments as $comment)
{
echo $comment->votes->count('vote');
}

Laravel 5.0 how to order an eloquent with() query

I have two models: item and faq. The are in a belongsToMany with each other with a correctly created join table: item_faq (singular of both). My join table has an additional field on it for order.
In my view I get all the faq's and if they have a pivot table record I output "checked" on a checkbox. I also have drag and drop ordering on the checkbox list and that works well.
A few code notes:
// ITEMS MODEL
public function faqs(){
return $this->belongsToMany('App\Faq');
}
// FAQ MODEL
public function items(){
return $this->belongsToMany('App\Item');
}
public function hasItem($item) {
$items = $this->items->lists('id');
return in_array($item, $items);
}
Schema of join table:
item_id
faq_id
order
timestamps
My issue is that they faq's don't load sorted by the order column on the pivot table.
I am using a very simple:
$faqs = \App\Faq::with('items')->get();
To retrieve the FAQ's and this works at getting all the faq's and if they are related, it checks the checkbox.
How can I order these by the order column on the join table?
Have a look at Eager Load Constraints and I think it will help provide a solution. From the docs:
Of course, eager loading Closures aren't limited to "constraints". You may also apply orders:
$users = User::with(['posts' => function($query) {
$query->orderBy('created_at', 'desc');
}])->get();

Resources