Laravel 5.2 Eloquent - Model through Many-To-Many Relationship - laravel

I have 3 models: Environments, Sites and Incidents. Each have their respective tables.
My model relationships are defined as follows:
Environments have many Sites (environment_id in sites table)
public function sites()
{
return $this->hasMany('App\Site');
}
Sites belong to an Environment (environment_id in sites table) and belong to many Incidents (incident_site relationship table with incident_id and site_id)
public function environment()
{
return $this->belongsTo('App\Environment');
}
public function incidents()
{
return $this->belongsToMany('App\Incident');
}
Incidents belong to many Sites (incident_site relationship table with incident_id and site_id)
public function sites()
{
return $this->belongsToMany('App\Site');
}
Problem: I am trying to retrieve a collection of all Site Incidents through the Environment model like this:
$environment->incidents()->count();
The only way I've been able to get it to work in a controller so far is like this:
$environment->load(['sites.incidents' => function ($q) use ( &$incidents ) {
$incidents = $q->orderBy('id','desc')->get();
}]);
But it's not ideal to work with in other areas of the App.
Question: How do I go about making the above relationship work through a method in the Environment model? Is there an easier way?

There isn't any provision for using hasManyThrough() in many-to-many relation. But you can achieve this by using either DB::raw() or you can add following function to your BaseModel as given in this forum.
public function manyThroughMany($related, $through, $firstKey, $secondKey, $pivotKey)
{
$model = new $related;
$table = $model->getTable();
$throughModel = new $through;
$pivot = $throughModel->getTable();
return $model
->join($pivot, $pivot . '.' . $pivotKey, '=', $table . '.' . $secondKey)
->select($table . '.*')
->where($pivot . '.' . $firstKey, '=', $this->id);
}
Update: Use
first you would need to create a Model for incident_site
class incident_site extends Model{
public $table = 'incident_site';
//your other code
}
In your Enviorment model add the Incidents() method:
public function Incidents()
{
return $this->manyThroughMany('App\Incident', 'App\incident_site', 'site_id', 'id', 'incident_id');
}
Update:
Have modified the function according to your needs.
Change your function to following:
public function manyThroughMany($related, $through ,$middle , $firstKey, $secondKey, $pivotKey)
{
$model = new $related;
$table = $model->getTable();
$throughModel = new $through;
$pivot = $throughModel->getTable();
$middleModel = new $middle;
$middleModelIds = $middleModel->where($this->getForeignKey(),$this->getKey())->get()->lists('id')->toArray();
//$middleModelIds = $this->with($middleModel)->where()->get()->lists('id')->toArray();
//$middleModelIds = $this->sites()->get()->lists('id')->toArray();
return $model
->join($pivot, $pivot . '.' . $pivotKey, '=', $table . '.' . $secondKey)
->select($table . '.*')
->whereIn($pivot . '.' . $firstKey,$middleModelIds);// '=', $this->id);
}
Use:
Extra argument of middle table needs to passed.
public function Incidents()
{
return $this->manyThroughMany('App\Incident', 'App\incident_site','App\Site','site_id', 'id', 'incident_id');
}

Related

eloquent with() in multiple where() clouse

I am trying to filter results of relationship table.
public function read_projects_by_coords(Request $request)
{
$from_lat = $request->get("from_lat");
$to_lat = $request->get('to_lat');
$from_lng = $request->get('from_lng');
$to_lng = $request->get('to_lng');
$projects = Project::with(["details" => function($query) use ($from_lat, $from_lng, $to_lat, $to_lng){
return $query->where("details.lat", ">", $from_lat)
->where("details.lat", "<", $to_lat)
->where("details.lng", ">", $from_lng)
->where("details.lng", "<", $to_lng);
}])->get();
return response()->json($projects);
}
But when I run the above details(child) coming with a empty/null result and parent/Project table not filtered. I returns all...
For example $projects = Project::with(["details"])->get(); this is works without a problem. But when I try to filter Project Model with the where inside the with() I can't get the detail object records, and parent is not filtered.
to anyone who wants to see the models parent and child
class Project extends Model
{
protected $table = "projects";
protected $guarded = [];
protected $with = ["details"];
public function details(){
return $this->hasOne("App\Models\Detail");
}
}
class Detail extends Model
{
protected $table = "details";
protected $guarded = [];
public function project(){
return $this->belongsTo("App\Models\Project");
}
}
What am I am missing?
To filter the Project table to only select the ones with some Details matching your parameters, you need to use whereHas. You need to keep your with clause too in order to have the details property correctly populated.
I would use a callback to not repeat the same conditions
$callback = function($query) use ($from_lat, $from_lng, $to_lat, $to_lng) {
$query->where("lat", ">", $from_lat)
->where("lat", "<", $to_lat)
->where("lng", ">", $from_lng)
->where("lng", "<", $to_lng);
}
$projects = Project::with(['details' => $callback])
->whereHas('details', $callback)
->get();
return response()->json($projects);

non trivial relationship in eloquent

i get:
Relationship method must return an object of type
Illuminate\Database\Eloquent\Relations\Relation
code of model:
class Order extends Model{
public function order_status(){
$q = self::GetQueryWithCurrentOrderStatus();
return $q->where('order.id', '=', $this->id)->get();
}
private static function GetQueryWithCurrentOrderStatus(){
$rawSql = OrderOrderStatus::selectRaw('order_order_status.order_id as id, max(created_at)')->groupBy('order_order_status.order_id')->toSql();
$query = OrderStatus::join('order_order_status', 'order_order_status.order_status_id', '=', 'order_status.id')
->join('order', 'order.id', '=', 'order_order_status.order_id')
->join(DB::raw('( ' . $rawSql . ') CurrentOrderStatus'), function ($join) {
$join->on('order_order_status.id', '=', 'CurrentOrderStatus.id');
});
return $query;
}
}
db structure is written in the answer here:
https://dba.stackexchange.com/questions/151193/good-database-structure-for-scenario-with-orders-that-have-a-state-and-the-state/151195#151195
order_status_history is order_order_status
now i could write in the blade file just:
$order->order_status() instead of $order->order_status ... but why? is there a solution?
If you're trying to call a method, call a method. order_status isn't a property.
If you access it as a property, it requires an Eloquent relationship (like it says) which are created through the hasOne, hasMany, belongsTo, belongsToMany methods: https://laravel.com/docs/master/eloquent-relationships

where clause inside a relationships - laravel

I have 3 models like this:
WarehousePivotCategory:
id - warehouse_id - warehouse_category_id
Warehouse:
title
WarehouseCategory:
title_en
I've created 2 hasOne relationships inside WarehousePivotCategory and they work fine:
public function Warehouse()
{
return $this->hasOne('App\Models\Warehouse','id','warehouse_id');
}
public function WarehouseCategory()
{
return $this->hasOne('App\Models\WarehouseCategory','id','warehouse_category_id');
}
in the database I have two records in warehouses table :
id title
1 AA
2 BB
I want to search title in warehouses :
$title = 'AA';
$warehouses = WarehousePivotCategory::with(['warehouse' => function($q) use ($title) {
$q->where('title', 'like', '%' . $title . '%');
},'WarehouseCategory'])->get();
foreach ($warehouses as $w)
{
echo $w->warehouse->title; // no thing
}
but it doesn't return any of title of warehouses.
my relationships is correct because below code works fine :
WarehousePivotCategory::with('warehouse','WarehouseCategory')->paginate(10);
I think you're missing get method in your closure. Try it like this:
$warehouses = WarehousePivotCategory::with(['warehouse' => function($q) use ($title) {
$q->where('title', 'like', '%' . $title . '%')->get(); },'WarehouseCategory'])->get();
You can also send array of fields you want to fetch to get method, like this:
$warehouses = WarehousePivotCategory::with(['warehouse' => function($q) use ($title) {
$q->where('title', 'like', '%' . $title . '%')->get(['id', 'title']); },'WarehouseCategory'])->get();
That is wrong. You don't need to use hasone while you have created pivot.You need to use BelongsToMany
class warehouse extends Model{
public function ware_cat(){
return $this->BelongsToMany('App\Models\WarehouseCategory');
}
public function getWarehouse(){
$this->with('ware_cat')->get();
}
}
Pivot table will fetch it so in warehouse model you will get its category and in category model you will get the warehouse same way visa-versa.

Laravel Error: Trying to get property of non-object

I am getting an error
Trying to get property of non-object (View: C:\xampp\htdocs\laravel\proj\resources\views\mycases.blade.php)
I have defined a relationship between two models Ccase and Hiring.
public function hirings()
{
return $this -> hasMany('App\Hiring', 'case_ID')->orderBy('id','desc');
}
and paginating the results using a method below
public function getHiringsPaginateAttribute($perPage)
{
return $this->hirings()->paginate($perPage);
}
The other model 'Hiring' has a method to define relationship with Ccase as follows:
public function ccase()
{
return $this->belongsTo('App\Ccase', 'id');
}
In my controller, I have following code:
if(isset($search_term))
{
$search_term = preg_replace('/\s+/', ' ', $search_term);
$search_term = trim($search_term);
if (strlen($search_term) > 0 && strlen(trim($search_term)) == 0)
$search_term = NULL;
$search_terms = explode(' ',$search_term);
$fields = array('id', 'title', 'case');
$hirings = $hirings->whereHas('ccase', function($q) use ($search_terms, $fields){
foreach ($search_terms as $term)
{
foreach ($fields as $field)
{
$q->orWhere($field, 'LIKE', '%'. $term .'%');
}
}
});
}
$hirings = $hirings->getHiringsPaginateAttribute($results_per_page);
In mycases.blade.php, my code is
{{$hiring->ccase->id}}
This line is throwing the above said error while the output of {{$hiring->ccase}} is:
{"id":1,"case":"HI this is a sample case i am putting just for test.","created_at":"2015-02-22 11:54:09","updated_at":"2015-02-22 11:54:09"}
What might be wrong with the code?
Unfortunately, you can't use related models in views. Here's the detailed explanation why.
Your case can be solved by specifying the name of the associated column on the parent table:
return $this->belongsTo('App\Ccase', 'ccaseId', 'id');
In model Hiring, it will be like
public function ccase()
{
return $this->belongsTo('App\Ccase', 'case_ID', 'id');
}
And now in view use it like this:
{{ $hiring->ccase->id }}
Not sure, but i think that you could use relations in view, you should use an eager loading in controller where you are quering for $hirings, just add a:
with(['hirings.ccase'])
Could you please provide a peace of code from controller where you make a query to Hiring model for clear?

Laravel 4 unable to retrieve value from belongsTo relation using query builder

I have two tables, organizations and categories. This is how my tables and models are set up:
Tables:
organizations
id - integer
category_id - integer, fk
name - string
...
categories
id - integer
name - string
Models:
class Organization extends Eloquent
{
public function category()
{
return $this->belongsTo('Category');
}
public function comments()
{
return $this->morphMany('Comment', 'commentable');
}
}
class Category extends Eloquent
{
public $timestamps = false;
public function organization()
{
return $this->hasMany('Organization');
}
}
In my routes.php file, I have the following code (where $category is equal to "organizations"):
$query = Organization::query()
->join('categories', 'categories.id', '=', $category . '.id')
->select('categories.name as category');
$tiles = $query->get();
In my view, I am able to perform the following actions without any errors:
foreach($tile->comments as $comment)
{
...
}
...
$tile->name;
However, calling $tile->category->name will give me the error Trying to get property of non-object. And calling $tile->category will just return null. How can I display the category associated with the organization? I am able to retrieve other properties and relations just fine, but this is giving me a problem.
You have a bug in your code: $category . '.id' should be $category . '.category_id' doing so should make $tile->category->name work.
Also please note (you probably already know this) that in the code that you provided you are not actually using the belongsTo relation as set in your Organization class, you are just using the joined data.
The following would also work using the Eloquent ORM utilizing the models relationship methods:
$tiles = Organization::with('category')
->get();
foreach ($tiles as $tile)
{
echo $tile->name . '<br>';
echo $tile->category->name . '<br>';
}
Or you could do so the other way round from the categories model like so:
$tiles = Category::with('organization')
->get();
foreach ($tiles as $tile)
{
echo $tile->name . '<br>';
foreach($tile->organization as $org)
{
echo $org->name . '<br>';
}
}

Resources