How do I use join with Eloquent taking in consideration the following table structure:
I have a properies table
---------------------
ID | Name
---------------------
1 | Property Name
than I have rooms
----------------------
RoomID | Property
----------------------
A-212 | 1
----------------------
F-1231 | 1
here Property is the foreign key
than I want to get all Properties and count how many rooms do they have each
The query which retrives all looks like
class PropertiesRepository extends EloquentBaseRepository implements PropertiesInterface
{
use TaggableRepository;
/**
* Construct
* #param Properties $properties
*/
public function __construct( Properties $properties )
{
$this->model = $properties;
}
/**
* Get all properties joining rooms
* #return Properties
*/
public function getAll()
{
return $this->model->get();
}
}
How do I extend this query to get the desired result?
This is more of a MySQL join+group+select trick which includes following steps.
Join your relation table(use join if you want to exclude rows with RoomsCount=0, else use leftJoin)
Use groupBy by primaryKey to avoid duplicates of the join.
Select count of joined table
$this->model->leftJoin('Rooms', 'Properties.ID', '=', 'Rooms.Property')
->selectRaw('Properties.*, count(Rooms.RoomID) as RoomsCount')
->groupBy('Properties.ID')
->get();
Define the relationship on your Property model class:
<?php
namespace App\Models;
class Property extends Model {
public function rooms() {
return $this->hasMany(Room::class);
}
}
$properties = Property::withCount(['rooms'])->get();
This will add a rooms_count to the result.
Related
I have a simple question. Could anyone help me please?
In laravel models, we can use Sotfdeletes trait. So this trait add the whereNull('deleted_at') sentence. It is ok. However, we have a big table and our queries are very slow.
We have Company model which use SotfDeletes trait. It has some scope methods such as createdAtBigger, nameLike, oneOfBoard etc.
This is the company model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Query\Builder;
/**
* #method static|Builder createdAtBigger(\DateTime $datetime)
* #method static|Builder nameLike(string $name)
* #method static|Builder oneOfBoard(array $boardIds)
*/
class Company extends Model
{
use SoftDeletes;
public function scopeCreatedAtBigger(Builder $builder, $datetime)
{
return $builder->where('created_at', '>', $datetime);
}
public function scopeNameLike(Builder $builder, $name)
{
return $builder->where('name', 'LIKE', '%'.$name.'%');
}
public function scopeOneOfBoard(Builder $builder, $boardIds)
{
return $builder->whereIn('board_id', $boardIds);
}
/**
* the other methods and scopes
* ...
* ...
* ...
*/
}
When we try to use that model with these scopes, it likes that for example:
$companies = Company::nameLike('XX Company')
->oneOfBoard([1,2,3,4])
->createdAtBigger('2022-02-14')
->get();
and its sql is like that:
SELECT *
FROM company
WHERE name like '%XX Company%'
and created_at > '2022-02-14'
and board_id in(1,2,3,4)
and deleted_at=null;
You can see, the deleted_at clause is the last condition.
This table has mostly deleted rows.
I want to move deleted_at condition at the first.
If my sql will like that, it it will take half time than now:
But I couldn't.
SELECT *
FROM company
WHERE deleted_at=null
and name like '%XX Company%'
and created_at > '2022-02-14'
and board_id in(1,2,3,4);
I don't want to use deletedAtNotNull scope because this model is very huge. It has lots of calls.
How can I do it? Is that any way?
PS: Thanks for #timlewis, I have an index on deleted_at column.
I would like to create a withCount subquery for this model.
Thanks to Iqbal Butt
I have this snippet for getting the count.
$count = Action::select('article_id as assigned');
if (!empty($action_type_id))
{
$count = $count->where('action_type_id', $action_type_id);
}
if (!empty($set_id))
{
$count = $count->where('set_id', $set_id);
}
$count = $count->distinct('article_id')->count('article_id');
I would like to execute it like this, but I feel this is painfully flawed.
Clarification edit
I have a many to many relationship of sets to articles.
Each article has a number of actions.
The actions can have a variety of action types.
I need to count the types of actions for each article in a given set.
$sets = Set::withCount(['actions' => function ($q) use ($action_type_id, $set_id) {
$q->select('article_id as assigned');
if (!empty($action_type_id))
{
$q->where('action_type_id', $action_type_id);
}
if (!empty($set_id))
{
$q->where('set_id', $set_id);
}
$q->distinct('article_id')
->count('article_id');
// I believe this is executing inner query
}])
->get();
return $sets;
This gives me the error, more then likely because the inner query is executing and without the outer query.
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'sets.id' in
'where clause' (SQL: select count(distinct article_id) as aggregate
from actions where sets.id = actions.set_id and
action_type_id = 1 and set_id = 1)
Edit per comments
Article Model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Article extends Model
{
/**
* Get the sets for the article.
*/
public function sets()
{
return $this->belongsToMany(Set::class);
}
public function actions()
{
return $this->hasMany(Action::class);
}
}
Set Model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Set extends Model
{
/**
* Get the articles for the set.
*/
public function articles()
{
return $this->belongsToMany(Article::class);
}
public function actions()
{
return $this->hasMany(Action::class);
}
}
Action Model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Action extends Model
{
/**
* Get the set that owns the action.
*/
public function set()
{
return $this->belongsTo(Set::class);
}
/**
* Get the article that owns the action.
*/
public function article()
{
return $this->belongsTo(Article::class);
}
}
Database
actions
id
set_id
article_id
action_type_id
sets
id
name
articles
id
name
article_set
id
set_id
article_id
Here is something that will get the data you need although it uses the returned collection to calculate the count.
$articles = Set::find($set_id)
->articles()
->with('actions')
->get()
->map(function($article){
$article->action_type_count = $article->actions->unique('action_type')->count();
return $article;
});
This will give you a collection of articles. The action type count is in a property action_type_count on each article in the collection. So to get the first article's action type count:
$articles->first()->action_type_count;
User model has many friends primary key id
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
public function friends()
{
return $this->hasMany(Friend::class);
}
}
Friend model; belongs to user. Foreign key user_id.
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Friend extends Model
{
protected $guarded = ['id'];
//Make sure birthday is always in right format
public function setBirthdayAttribute($value)
{
//Put in mysql format
$this->attributes['birthday'] = date("Y-m-d",strtotime($value));
}
public function user()
{
return $this->belongsTo(User::class);
}
}
Error when updating user with following controller code:
/**
* Update the specified resource in storage.
*
* #param \Illuminate\Http\Request $request
* #param \App\Friend $friend
* #return \Illuminate\Http\Response
*/
public function update(Request $request, Friend $friend)
{
$friend->fill($request->all());
//Assign user to logged in user...does NOT work; things it is a column
$friend->user = \Auth::user();
$friend->save();
return redirect(action('FriendsController#index'));
}
Error message when saving:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'user' in 'field list' (SQL: update `friends` set `last_name` = Muench!!, `updated_at` = 2018-09-28 13:26:01, `user` = {"id":1,"name":"Chris","email":"me#chrismuench.com","email_verified_at":null,"created_at":"2018-09-27 19:52:03","updated_at":"2018-09-27 19:52:03","braintree_id":null,"paypal_email":null,"card_brand":null,"card_last_four":null,"trial_ends_at":null} where `id` = 8)
Is there a reason I cannot assign user with a user object? I know I could set the user_id column but this would be nice if I could pass around objects.
If you want to assign object and not property in your controller you need to add the following mutator
public function setUserAttribute($user)
{
$this->attributes['user_id'] = $user->id;
}
to your Friend object.
However it could be quite risky, because relationship name is also user, so it's better to use other name (for example friend_user and then method name setFriendUserAttribute) or to use Eloquent associate method instead:
$friend->user()->associate(\Auth::user());
I need my model to return only those records from one table where a matching record does not exist in another table. I was thinking that the solution might be with Query Scopes but the documentation only scratches the surface. So my SQL would look something like this:
SELECT *
FROM A
WHERE NOT EXISTS (SELECT A_id FROM B
WHERE B.A_id = A.id)
Here are my tables:
A
-------------
| id | name |
-------------
B
--------------------
| id | A_id | name |
--------------------
Probably unnecessary but here are my models. Model for A:
class A extends Eloquent{
public function B(){
return $this->hasOne('B', 'A_id', 'id');
}
}
Model for B:
class B extends Eloquent{
public function A(){
return $this->belongsTo('B', 'A_id', 'id');
}
}
Something like
A::whereNotExists(function($query)
{
$query->select(DB::raw(1))
->from('B')
->whereRaw('A.id = B.id');
})
->get();
I would like to create a relationship between 2 tables with eloquent but i can't find exactly how to proceed...
Here are my 2 models with relationship :
Table "etablissement":
<?php class Etablissement extends Eloquent {
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'etablissement';
public function annulation()
{
return $this->hasMany('Annulation');
}}
Table "annulation":
<?php class Annulation extends Eloquent {
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'annulation_remboursement';
public function etablissement ()
{
return $this->belongsTo('Etablissement');
}}
In the "Etablissement" table there is an id for each etablissement (id_etablissement) and in the "annulation" there is a column with the id_etablissement. How can i return in my controller a relation in order to have the etablissement's name with the annulation->id_etablissement :
class AnnulationsController extends \BaseController {
/**
* Display a listing of the resource.
*
* #return Response
*/
public function index()
{
}
It should be something like this within your index method:
$annulation = Annulation::find(1);
$annulation->etablissement->name
The annulation_remboursement table should have a establissement_id field.
Perhaps the error may be in the keys of the relation.
In https://laravel.com/docs/4.2/eloquent#one-to-one we see:
Take note that Eloquent assumes the foreign key of the relationship based on the model name. In this case, Phone model is assumed to use a user_id foreign key. If you wish to override this convention, you may pass a second argument to the hasOne method. Furthermore, you may pass a third argument to the method to specify which local column that should be used for the association:
return $this->hasOne('Phone', 'foreign_key');
return $this->hasOne('Phone', 'foreign_key', 'local_key');
[...] One To Many: Again, you may override the conventional foreign key by passing a second argument to the hasMany method. And, like the hasOne relation, the local column may also be specified:
return $this->hasMany('Comment', 'foreign_key');
return $this->hasMany('Comment', 'foreign_key', 'local_key');
* You should also checkout Defining The Inverse Of A Relation at the same page.
So, in your case you have a key named id_etablissement but Laravel is searching for etablissement_id. If you wish to override this behaviour and specify a key you should do something like:
protected $table = 'etablissement';
public function annulation()
{
return $this->hasMany('Annulation','id_etablissement');
}
and according to "The Inverse Of A Relation"
protected $table = 'annulation_remboursement';
public function etablissement ()
{
return $this->belongsTo('Etablissement','id_etablissement');
}
Note that I didn't put any of the local keys, but those will be the third parameter of the relation.