Laravel 5.0 Eloquent 3rd level relationships - laravel-5

i have three tables
Property:
property_id
title
Property Prices:
property_price_id
currency_id
price
currencies
currency_id
title
Property Model:
public function PropertyPrice(){
return $this->hasMany('App\PropertyPrice','property_id');
}
property Price Model:
public function Currency(){
return $this->belongsTo('App\Currency','currency_id');
}
public function Property(){
return $this->belongsTo('App\Property','property_id');
}
Currency Model
public function PropertyPrice(){
return $this->hasMany('App\PropertyPrice','currency_id');
}
Now i am running this Eloquent command for getting properties with prices,
Property::with('PropertyPrice')->orderBy('ordering','desc')->paginate(10);
but i am confused how to get the currency title? please note i have seen the example provided on official documentation under "Has Many Through" but its a different example , i am unable to relate to it.
please help, thanks

$properties = Property::with('PropertyPrice')->orderBy('ordering','desc')->paginate(10);
then
foreach ($properties as $property) {
$currency_title = $property->PropertyPrice->Currency->title;
}
using the same logic, you could do that in your view as well

i was doing some experiments and found out the answer,
Property::with(['PropertyPrice' => function($query){
$query->with('Currency');
}
])->orderBy('ordering','desc')->paginate($per_page);

Related

Laravel, many-to-many relationship among multiple models

I Have multiple models that have many to many relationship
Here are the models
News Section
Categories
Sub Categories
Approved News
Pending News
Each News Section can have multiple Categories.
Each Category can have multiple Sub Categories.
Each Sub Category can has multiple Approved News and Pending News.
I want to have News with Categories, Sub Categories and Pending / Approve news
and stuff like
Categories with Sub Categories and Approve news
I tried with pivot tables but not able to get results
Models are as follow
News Section
class NewsSection extends Model
{
public function categories()
{
return $this->belongsToMany(Category::class);
}
}
Category
class Category extends Model
{
public function subcats(){
return $this->belongsToMany(SubCategory::class);
}
public function newssections(){
return $this->belongsToMany(NewsSection::class);
}
}
SubCategory
class SubCategory extends Model
{
public function category(){
return $this->belongsTo(Category::class);
}
public function approvednews(){
return $this->belongsToMany(ApprovedNews::class);
}
public function pendingnews(){
return $this->belongsToMany(PendingNews::class);
}
}
ApprovedNews
class ApprovedNews extends Model
{
public function subcategories (){
return $this->belongsToMany(SubCategory::class);
}
}
PendingdNews
class PendingdNewsextends Model
{
public function subcategories (){
return $this->belongsToMany(SubCategory::class);
}
}
Update
This what I have done so far
$news =Category::with('subcats.approvednews')->where('id',1)->get();
I got all the approved news with subcategories and categories
how can i modify this to get specific subcats and approved news per category, if i do this
$news =Category::with('subcats.approvednews')->where('subcats.id',1)->get();
I get an error like id ambiguous
Is it possible to pick and chose items from relation for instance return just 2 subcats and 3 approved news for each subcat of selected category
or
get count of approved news and pending news per subcat and category
Thanks in advance
The error "error like id ambiguous" means that you need to specify the table in your where('id', 1) like where('table.id', 1) so that MySQL knows which id column in which table you mean.
You can constrain the models returned by with like this:
Category::with(['subcats' => function(Builder $query) {
$query->where('id', '=', 1);
}]);
Also you can count relations:
$subcat = SubCategory::withCount(['approvednews']);
$subcat->approvednews_count;
Limiting eager loaded relations is not possible per the docs.
A workaround may be to go the other way round starting from ApprovedNews:
ApprovedNews::whereHas(['subcategories' => function(Builder $query) {
$query->where('id', '=', 1);
}])->limit(10);
I have a few suggestions of how you can get this to work. In your comments, you say you are getting an issue doing the following:
$items=Category::with('subcategory')->where('id',1)->get();
Where is 'subcategory' coming from? By the looks of your model, your relationship between Category and Subcategory is called subcats. So you would need to change it to:
$items=Category::with('subcats')->where('id',1)->get();
And if you dump that out, you should see that you will get the category where the ID is 1, and the subcategories loaded in. A way to test that your relationships are working would be something like this:
$category = Category::find(1);
$subCats = $category->subcats()->get();
dd($subCats);
In your relationships, instead of using SubCategory::class I would suggest trying return $this->belongsToMany('App\SubCategory'); so that the models are definitely connected.
Once you have tested that your relationships between one another work, you can get started on testing that you can go from a->b->c etc.
May be using "Nested Eager Loading" and "scope", you can do something like
$pendings = NewSection::with('categories.subCategories')->pending()->get()
$approved = NewSection::with('categories.subCategories')->approved()->get()
not tested it, but you can try, may be with some modification, you can reach to your goal.
if you want return one collection, you may like to merge it
$approved->merge($pendings);
but, you should avoid it.

How to retrieve multiple relations with multiple tables in laravel eloquent

I'm using Laravel 5.8 to build a babysitting site. I have 4 tables with different relationships as below:
please see this image
The relationships are:
Babysitter->hasMany(session)
Sessions->hasOne(Review)
Sessions->hasOne(Kids)
Sessions->hasOne(Babysitter)
Sessions->hasOne(Parent)
I want to achieve 2 things:
First one
I want to show this result when listing all babysitters. I'm showing this information for each babysitter:
plsease see this image
See here what I couldn't achieve
plsease see this image
This is my code
Sitters::where('Status', 'active')->where('Verified', 1)->get();
Second one
Also, I've tried to show kids name with parent review as shown here:
plsease see this image
This is what i'm using
Sessions::select('Reviews.*', 'Sessions.Parent_id')->join('Reviews', 'Reviews.Session_id', '=', 'Sessions.id')->with('owner')->where('Trainer_id', session('user')->Id)->where('Status', '=', 'complete')->with('owner')->orderBy('Sessions.id', 'DESC')->get();
Here is Session.php Model
public function owner(){
return $this->belongsTo('App\Models\Parents', 'Parent_id');
}
As discussed change the relations:
Babysitter->hasMany(sesstion)
Sessions->hasOne(Review)
Sessions->belongsTo(Kids)
Sessions->belongsTo(Babysitter)
Sessions->belongsTo(Parent)
First one
in Babysitter.php declare the following attributes
class Babysitter extends Model
{
public function reviews()
{
$this->hasManyThrough(Review::class, Session::class);
}
public function getAverageReviewAttribute()
{
return $this->reviews()->avg('Rating');
}
}
Then you just need to call it on the model instance.
$babysitter = Babysitter::first();
return $babysitter->average_review;
Second one
Just use the relation
$babysitter = BabySitter::with(['sessions' => public function ($session) {
$session->with(['review','parent','kids']);
})->where('trainer_id', '=', session('user')->Id) //did not understand this condition
->first();
This assumes you have parent, kids and review relation declared on Session::class. (change the names if needed)
After a few days of searching & testing, this is what worked for me:
Inside (Sitters) Model, put this relation
public function sessions()
{
return $this->hasMany(Sessions::class, 'sitter_id')
->withCount('reviews')
->withCount(['reviews as review_avg' => function($query){
$query->select(DB::raw('AVG(Rating)'));
}]);
}
Also, inside (Sessions) Model, put this relation
public function reviews()
{
return $this->hasOne(Reviews::class, 'Session_id');
}
Now you query like this
return $sitters = Sitters::with('sessions')->get();
I hope this can help someone :)

Complex relationships in Laravel (hasManyThrough through a pivot table)

So I have four tables.
products
skus
sku_attribute
attributes
Each product has many skus. Each sku has many attributes. And each attribute can have several skus associated with it. sku_attribute is a pivot table for the many to many relationship between skus and attributes.
This works fine! But now, how do I get all the attributes associated with a product?
The following code worked for me.
public function attributes()
{
return $this->skus->flatMap(function ($sku) {
return $sku->attributes;
});
}
But I get an error because this doesn't return a relationship but rather a collection.
I also tried using the solution found here. But I couldn't get it to work properly because their model is slightly different than mine. (Each product has many skus, not vice versa.)
Another solution was to include a third column on sku_attributes for the product_id, but I couldn't find a way to default fill this to sku->product->id on the $sku->attach($attribute_id) method. Instead I'd have to manually call this every time, like $sku->attach($attribute_id, ['product_id' => $sku->product->id]).
You could try something like
public function attributes()
{
return Attributes::whereIn('id', function($query){
return $query->whereHas('skuAttributes', function($query) {
return $query->whereHas('products', function($query) {
return $query->where('id', $this->id);
);
});
});
}
Then on the skuAttributes model create a hasManyThrough to products.
There is no native support for this in Laravel.
I created a package for it: https://github.com/staudenmeir/eloquent-has-many-deep
You can use the relationship like this:
class Product extends Model
{
use \Staudenmeir\EloquentHasManyDeep\HasRelationships;
public function attributes()
{
return $this->hasManyDeep(Attribute::class, [Sku::class, 'sku_attribute']);
}
}
This ended up working for me.
public function attributes()
{
return $this->skus->flatMap(function ($sku) {
return $sku['attributes'];
});
}

Laravel / Eloquent: Search for rows by value in polymorphed table

I'm stuck at the moment and hope someone can give me a hand. I'm using a polymorphic relation and want to search my database for rows that fulfill conditions in the "parent" and the "child" table.
To get concrete, one small example. Given the following structure I e.g. want to look for a property with price "600" and rooms "3". Is there a way to do that with eloquent?
Tables
Table properties (parent)
id
price
details_type [can be "Apartment" or "Parcel"]
details_id
Table apartments (child)
id
rooms
Table parcels (child)
id
... (does not have a "rooms" column)
Relationships
Class Property
public function details() {
return $this->morphTo();
}
Classes Apartment + Parcel
public function property() {
return $this->morphMany('Property', 'details')
}
What I tried
A lot, really. But somehow I'm always doing something wrong or missing something. The solutions that, in my opinion should work are either:
Property::with(array('details' => function($query) {
$query->where('rooms', 3);
}));
or
Property::with('details')
->whereHas('details', function($query) {
$query->where('rooms', '=', '3');
});
But in both cases I get the following FatalError.:
Class name must be a valid object or a string
Has anyone of you already had a similar problem? Thank you very much for any kind of hint.
Let's start with your naming convention:
public function detail() // It relates to a single object
{
return $this->morphTo();
}
And
public function properties() // It relates to several objects
{
return $this->morphMany('Property', 'details')
}
Then you would be able to do this:
$properties = Property::whereHas('details', function($q)
{
$q->where('rooms', '=', '3');
})
->where('price', '=', 600)
->get();
Please note that this will never return a Parcel, since there isn't a parcel with a room.

Laravel 3 Eloquent ORM usage

I have the following code which works but doesn't seem to follow the laravel eloquent way:
Article::left_join('images', 'articles.id', '=', 'images.article_id')
->join('article_category', 'articles.id', '=', 'article_category.article_id')
->where('article_category.category_id', '=', $category_id)
->get();
I have 4 tables; articles and categories which have a many to many relationship with each other, a pivot table article_category table which holds the article id and category id and an image table which has one to one relationship with an article.
I setup my models as:
class Category extends Eloquent {
public static function get_articles($category_id) {
return static::find($category_id)->has_many_and_belongs_to('Article');
}
class Article extends Eloquent {
public function categories() {
return $this->has_many_and_belongs_to('Category');
}
public function image() {
return $this->has_one('Image');
}
However I can't seem to get all three bits of info together. I can do:
Category::get_articles($current_category)->get();
To get all articles in a given category but I can't seem to get the image for the article, there seems to be nothing I can chain onto? Unless I'm doing it incorrectly? Is there a trick I'm missing?
I even tried the stripped down version from the docs:
foreach (Article::with('image')->get() as $article) {
echo $article->image->foo;
}
However I get an error: Trying to get property of non-object, even though var_dump shows $article->image is an object! Weird.
Thanks
If you have not setup a model for the image table, do that. The ORM needs the model there so it knows what 'Image' refers to.
Can you get the category information using the ::with method or is that troublesome too?

Resources