Laravel relationships - 3-way table - php

I have 3 models: Basket, Product and Asset.
A Basket can contain many Assets, and one asset can appear in multiple Baskets.
A Product can have many Assets, but an Asset can also be assigned to more than one Product.
The aim is to get Assets into a Basket while still knowing which Product it was added from.
The table structure I envision having is this:
-------------------------------------
| basket_id | asset_id | product_id |
-------------------------------------
| 1 | 1 | 1 |
| 1 | 3 | 1 |
| 1 | 15 | 2 |
| 2 | 23 | 1 |
| 2 | 3 | 1 |
| 3 | 79 | 3 |
-------------------------------------
I also envision a model setup of something like this:
class Basket extends Eloquent {
public function assets() {
return $this->belongsToMany('Asset');
}
}
class Product extends Eloquent {
public function assets() {
return $this->belongsToMany('Asset');
}
}
class Asset extends Eloquent {
public function products() {
return $this->belongsToMany('Product');
}
public function product() {
// This should return the product from the product_id column
}
}
I'm not sure how to go about writing Asset::product() so that it returns the corresponding Product.
I'd like to call something like Basket::find(1)->assets()->first()->product; to get the product that the first asset was added to the basket from.

Here's a small piece that does what you need
https://github.com/jarektkaczyk/Eloquent-triple-pivot
and thanks to your PR a package on packagist:
https://packagist.org/packages/jarektkaczyk/eloquent-triple-pivot

Related

Three-way pivot Table Laravel

I'm struggling to connect three models in my Laravel app. The models are Bottle, Label and Spirit. I want to get all the labels based on bottle_id and spirit_id so I created a pivot table to store the relationships between Bottle-Label-Spirit. Please see below my current setup.
DB
+---------+--------+---------+-------------------------+
| bottles | labels | spirits | bottle_label_spirit |
+---------+--------+---------+-------------------------+
| id | id | id | id |
| name | name | name | bottle_id |
| | | | label_id |
| | | | spirit_id |
| | | | created_at |
| | | | updated_at |
+---------+--------+---------+-------------------------+
Where bottle_label_spirit is my pivot table
BOTTLE CLASS
class Bottle extends Model
{
public function labels() {
return $this->belongsToMany(Label::class)->withTimestamps();
}
public function spirits() {
return $this->belongsToMany(Spirit::class)->withTimestamps();
}
}
LABEL CLASS
class Label extends Model
{
public function bottles() {
return $this->belongsToMany(Bottle::class)->withTimestamps();
}
public function spirits() {
return $this->belongsToMany(Spirit::class)->withTimestamps();
}
}
SPIRIT CLASS
class Spirit extends Model
{
public function labels() {
return $this->belongsToMany(Label::class)->withTimestamps();
}
public function bottles() {
return $this->belongsToMany(Bottle::class)->withTimestamps();
}
}
QUESTIONS
So my questions are:
Is this the right approach to handle this manyToMany relationships?
If yes, how do i get all the labels where bottle_id = 1 and spirit_id = 1
You do not need a 4th table for this, you can accomplish all of this using hasManyThrough. This will allow you to query distant relationships.

Eloquent Multitable query

I trying to create a search function in php with Slim and Eloquent 5.1, but i have a problem to get the results from the database.
This are my tables
Listing Table
id | title | description | price | size |etc...
-----|--------|-------------|-------|--------|-------
1 | list1 | some text | 500 | 50 |
2 | list2 | some text | 700 | 80 |
3 | list3 | some text | 350 | 120 |
Listings Option Table
id | id_listing | opt_name | opt_value
-----|-------------|-------------|----------
1 | 1 | rooms | 3
2 | 1 | baths | 4
3 | 2 | rooms | 8
4 | 3 | baths | 1
5 | 3 | rooms | 6
Listings Images Table
id | id_listing | file_name
-----|-------------|-------------
1 | 1 | file_1.png
2 | 1 | file_2.png
1 | 2 | file_3.png
2 | 3 | file_4.png
Now i found a way to get all the results with all they options with
listado = $app->listing->with(['options'],['imgs'])->get();
My classes
//Listing Class
class Listing extends Eloquent{
protected $table = 'listings';
public function options(){
return $this->hasMany('Casas\Listing\ListingOption', 'id_listing', 'id');
}
public function imgs(){
return $this->hasMany('Casas\Listing\ListingImg', 'id_listing');
}
}
// ListingOption class
class ListingOption extends Eloquent{
protected $table = 'listings_options';
public function listings()
{
return $this->belongsTo('Casas\Listing\Listing', 'id_listing');
}
}
// ListingImg Class
class ListingImg extends Eloquent{
protected $table = 'listings_imgs';
public function listings()
{
return $this->belongsTo('Casas\Listing\Listing', 'id_listing');
}
}
but i cannot find a way to filter the query the way i want to. I tried many thing.
Example: I want to get all the listings that have price less than 600 and have more than 2 rooms.
So that be listing id 1 and 3
If anyone can help create this query i'll appreciate that.
What you want want to use is whereHas.
When accessing the records for a model, you may wish to limit your results based on the existence of a relationship
$result = $app->listing->with('options', 'imgs')
->whereHas('options', function ($query) {
$query->where('opt_value', '>', 2)
->where('opt_name', 'room');
})
->where('price', '<', 600)->get();
So what we are doing here is, eager loading options and imgs tables and fetching only the listings that have at least one option where opt_value is bigger than two and opt_name is room.
You can find more in info about querying relationships in here
I would try something like this:
$result = $app->listing->with(
['options' => function ($query) {
$query->where('opt_value', '>', 2);
}
],
['imgs'])->where('price', '<', 600)->get();
The last, outer where should be assigned to listing. The inner where, passed in a callback, should be assigned to options directly.
Take a look at the docs (the "Constraining Eager Loads" section).

Laravel Eloquent how to return one to many to many collection

Is it possible to get an eloquent relation based on a One->Many->Many relationship? In the below example, I would want to get all of the gallery_votes for a given user.
users
+----+---------------+
| id | user_name |
+----+---------------+
| 1 | bob |
| 2 | sam |
+----+---------------+
galleries
+----+---------------+-----------+
| id | gallery_name | user_id |
+----+---------------+-----------+
| 1 | Alaska Pics | 1 |
| 2 | Texas Pics | 1 |
| 3 | California | 2 |
| 4 | Cars | 2 |
+----+---------------+-----------+
gallery_votes
+----+---------------+--------+
| id | gallery_id | vote |
+----+---------------+--------+
| 1 | 1 | 1 |
| 2 | 1 | 1 |
| 3 | 1 | 1 |
| 4 | 2 | 1 |
+----+---------------+--------+
The relationships are setup as follows.
class User extends Model implements AuthenticatableContract, CanResetPasswordContract {{
...
public function galleries()
{
return $this->hasMany('App\Gallery');
}
}
and
class Gallery extends Model {
....
public function votes()
{
return $this->hasMany('App\GalleryVote');
}
}
$user->galleries returns a collection of associated galleries, so that works correctly. and $gallery->votes returns a collection of associate votes, so that also works correctly. But $user->galleries->votes gives
Undefined property: Illuminate\Database\Eloquent\Collection::$votes on line 1
Is what I'm trying to do even possible with straight Laravel? Or do I need to write the SQL directly?
The "has many through" relation provides a convenient short-cut for accessing distant relations via an intermediate relation.
class User extends Model implements AuthenticatableContract, CanResetPasswordContract {{
...
public function galleries()
{
return $this->hasMany('App\Gallery');
}
public function votes()
{
return $this->hasManyThrough('App\GalleryVote', 'App\Gallery');
}
}
Now $user->votes will return all votes for that user. Remember you need to create the GalleryVote eloquent model as well.
You can read more about that type of relation, as well as some example usage in the documentation.

Laravel sum() method on collection returning results for all items in table

Given the following table
gallery
+----+---------------+--------------+---------+
| id | gallery_title | viewcount | user_id |
+----+---------------+--------------+---------+
| 1 | Animals | 10 | 1 |
| 2 | Cars | 5 | 1 |
| 3 | Houses | 2 | 2 |
+----+---------------+--------------+---------+
user
+----+----------+
| id | username |
+----+----------+
| 1 | Bob |
| 2 | James |
+----+----------+
and the following classes
class Gallery extends Model
....
public function user()
{
return $this->belongsTo('App\User');
}
and
class User extends Model
....
public function galleries()
{
return $this->hasMany('App\Gallery');
}
calling $galleryCollections= Auth::user()->galleries; returns an array of collections in which I can iterate through
foreach ($galleryCollections as $galleryCollection)
{
$viewcount += $galleryCollection->viewcount;
}
print $viewcount; #returns 15
and so far everything works as expected, correctly up until this point.
However if I accidentally called $galleryCollection->sum('viewcount'), which is the last value from the iteration, the returned value is 17, as it's simply running the following SQL select sum('viewcount') as aggregate from 'gallery'.
I'm struggling to understand what exactly what is happening here. It's almost as if it's calling the 'sum()' method on the gallery class itself without passing in any 'where' values. I'd at least expect it to call the sum() method on the Collection, but instead it's going back to the database.
Is it because my Gallery class does not implement a 'sum()' method, and therefore it uses the Parent Model class and ignores the Gallery class?
If you want to count through sql, then:
Auth::user()->galleries()->sum('viewCount');
But, Auth::user()->galleries->sum('viewCount'); will sum on the collection Auth::user()->galleries.
Auth::user()->galleries() is a queryBuilder whereas Auth::user()->galleries is a collection of galleries of that user.

Laravel getting results from model belongsToMany relationship back to controller

I'm using Laravel 5 and Eloquent, I have 3 tables setup:
photos
+----+---------------+------------------+
| id | photo_name | photo_location |
+----+---------------+------------------+
| 1 | kittens.jpg | C:\kittens.jpg |
| 2 | puppies.jpg | C:\puppies.jpg |
| 3 | airplanes.jpg | C:\airplanes.jpg |
| 4 | trains.jpg | C:\trains.jpg |
+----+---------------+------------------+
photo_set (pivot table)
+------------+----------+
| set_id | photo_id |
+------------+----------+
| 1 | 1 |
| 1 | 2 |
| 2 | 3 |
| 2 | 4 |
+------------+----------+
sets
+----+----------------+
| id | description |
+----+----------------+
| 1 | cute animals |
| 2 | transportation |
+----+----------------+
I created a belongsToMany relationship in my photos and sets models to link these two together.
class Photo extends Model {
public function sets()
{
return $this->belongsToMany('App\Set');
}
}
and
class Set extends Model {
public function photos()
{
return $this->belongsToMany('App\Photo');
}
}
However I'm trying to reference the $Sets->photos() model function in my controller, so that given a set of id=1, I can return the photo_location value for each row [C:\kittens.jpg,C:\puppies.jpg], but I don't know how to access it..
Also, I can "sort of" access this information in the view with:
#foreach($sets as $set)
{{$set->images}}
#endforeach
but it looks like it only iterates through once and returns a json (?) of the necessary information, though I'm not sure how to parse that to regular HTML either.
So in short, I'm having trouble accessing this data (photo_location) from both the controller and the view
Use Collection::lists()
$locations = Set::find(1)->photos->lists('photo_location');
It will return an array ['C:\kittens.jpg', 'C:\puppies.jpg']
http://laravel.com/api/4.2/Illuminate/Database/Eloquent/Collection.html#method_lists
In your controller try this :
$result = PhotoSet::where('set_id', $set_id)
->with('sets')
->with('photos')
->get();
$photo_location = $result->photos->photo_location;
here PhotoSet is the model for photo_set table, because it is a pivot table we will be able to access both sets and photos table from it.

Categories