Eloquent Relationship Between Tables in Laravel - php

I have three tables:
collections which has id, name
genre_collection which has id, genre_id, collection_id
genres which has id, name
I want to retrieve data from collections with generes.
Collections Model
class Collections extends Model{
public function genres(){
return $this->hasMany('App\Models\GenreCollectionRelationships', 'genre_id' , 'id');
}
}
generic_collection
class GenreCollectionRelationships extends Model{
public function genre(){
return $this->hasOne('App\Models\Genres', 'id', 'genre_id');
}
}
Search Controller
class SearchController extends Controller{
$collection->genres;
foreach($collection->genres as $item){
$item->genre;
}
}
This code is working fine. And the output is
Actual
"genres": [{
"id": 1,
"genre_id": 1,
"collection_id": 1,
"created_at": "2019-02-07 17:13:36",
"updated_at": "2019-02-07 17:13:36",
"genre": {
"name": "Action",
"meta": null
}
}]
Is there any way i could directly get the output as shown below
Expected
"genres": [ {
"name": "Action",
"meta": null
}]
I tried hasManyThrough, belongsToMany but nothing worked out.
Note. I am on laravel 5.7
Thanks in advance.

You could build your own query to achieve what you are looking for. Try this:
$collection = Collection
::join('genres', 'genre.id', '=', 'collections.genre_id')
->select('collections.*', 'genres.name','genre.meta')
->get();

I find your code a bit hard to follow...
Let me try and see if I understood it correctly...
You basically have two models:
Model Collection saved in table collections
Model Genre saved in table genres
Since you have a many to many relationship between them, you need a third table to link the both of them together.
By naming convention, Laravel expects you to name it based on the two models, ordered alphabetically. So to create a link between collections and genres, you would need to create a table collection_genre which has a collection_id as a reference to the collections table, and likewise a genre_id to identify the linked genre.
You can then define your relationships as follows:
class Collection extends Model {
public function genres() {
$this->belongsToMany(\App\Models\Genre::class);
}
}
and
class Genre extends Model {
public function collections() {
$this->belongsToMany(\App\Models\Collection::class);
}
}
Now, I'm not sure what your controller looks like as the question has some invalid code to it, but I suspect you want to search the genres for a given collection.
Your code could like like this:
Class CollectionController extends Controller {
function getGenres(Collection $collection) {
return $collection->genres;
}
}
This would return the genres for the given collection.
If you want to format this, you could create an Eloquent Resource for this:
Class CollectionResource extends Resource {
public function toArray() {
return [
'name' => $this->name,
'meta' => $this->meta
];
}
}
In your controller you can then do:
Class CollectionController extends Controller {
function getGenres(Collection $collection) {
return CollectionResource::collection($collection->genres);
}
}

in your collection model
class Collections extends Model
{
protected $table='collections';
public $primaryKey='id';
protected $fillable = ['name'];
public function genres()
{
return $this->belongsToMany('App\Model\Genres','genre_collection','collection_id','genre_id')->withTimestamps();
}
}
in your genres model
class Genre extends Model {
protected $table='genres';
public $primaryKey='id';
protected $fillable = ['name'];
public function collections()
{
return $this->belongsToMany('App\Model\Collections','genre_collection','genre_id','collection_id')->get();
}
}

You are creating many to many relationship between collections and genre using genre_collection pivot table. In that case, belongsToMany is appropriate. And you don't need any model for genre_collection table.
Collections model
class Collections extends Model
{
public function genres(){
return $this->belongsToMany('App\Models\Genres', 'genre_collection', 'genre_id', 'collection_id');
}
}
Genres model
class Genres extends Model
{
public function collections(){
return $this->belongsToMany('App\Models\Collections', 'genre_collection', 'collection_id', 'genre_id');
}
}
SearchController
class SearchController extends Controller
{
foreach($collection->genres as $item){
$item->genre; // get genre info
}
}

I'm assuming that you want to access Generic directly from collection . If this is the case you can define a many-to-many relationship in collection model directly to generic model to access it . Please refer this : https://laravel.com/docs/5.7/eloquent-relationships#many-to-many . Sorry if I'm wrong

Related

Sort belongsToMany relation base on field in belongTo relation Laravel Eloquent

I have a scenario where User has a belongsToMany relation with PortalBreakdown, PortalBreakdown has a belongsTo relation with Portal. Portal has order column in it. I have a method listing_quota($id) in UserController which returns all breakdowns of the user. I want to sort these breakdowns based on order column of the portal. Below are the code of classes and a method I have tried.
class User extends Model {
protected $table = 'user';
public function listing_quota() {
return $this->belongsToMany('App\PortalBreakdown', 'user_listing_quota')->withPivot(['quota']);
}
}
class PortalBreakdown extends Model {
protected $table = 'portal_breakdown';
public function portal() {
return $this->belongsTo('App\Portal');
}
}
class Portal extends Model {
protected $table = "portal";
protected $fillable = ['name', 'description', 'order'];
}
Below is the method where I am trying to return sorted by order. I tried few things some of which can be seen in commented code but not working.
class UserController extends Controller {
public function listing_quota($id)
{
$user = User::with(['listing_quota' => function ($query) use ($id) {
// $query->sortBy(function ($query) {
// return $query->portal->order;
// });
}, 'listing_quota.portal:id,name,order'])->findOrFail($id);
// $user = User::with(['listing_quota.portal' => function ($q) {
// $q->select(['id', 'name',order']);
// $q->orderBy('order');
// }])->findOrFail($id);
return $this->success($user->listing_quota);
}
}
I also tried chaining orderBy directly after relation in Model class but that's also not working from me. Thank you in advance.
NOTE: I am using Laravel Framework Lumen (5.7.8) (Laravel Components 5.7.*)

Laravel: Sorting a collection with a many to many relationship

I have two tables: assessments and benchmarks. benchmarks has a field called content. There is a many to many relationship between them: assessment_benchmark. I want to sort a collection of records from the assessment_benchmark table by the content attribute of the corresponding benchmark. I have tried:
$sorted = AssessmentBenchmark::all()->sortBy(function($assessmentBenchmark){
return $assessmentBenchmark->benchmark->content;
});
But this just does not work (it just returns the original order). However, when I return $assessmentBenchmark->comment for example, it does work (comment is a field in assessment_benchmark).
The models look like this:
class AssessmentBenchmark extends Model
{
public function benchmark()
{
return $this->belongsTo(Benchmark::class);
}
public function assessment()
{
return $this->belongsTo(Assessment::class);
}
}
class Benchmark extends Model
{
public function assessments()
{
return $this->belongsToMany(Assessment::class);
}
}
class Assessment extends Model
{
public function benchmarks()
{
return $this->belongsToMany(Benchmark::class);
}
}
Well, you can use below query for sorting, I'm gonna use Assessment model, because, I'm never use pivot modal before. Actually, I never had pivot model..
$assessments = Assessment::with(["benchmarks"=>function($query){
$query->orderBy("content","DESC");
}])
With method aşso provide you eagerloading, so when you put $assessments in iteration , you won't make new query for each relation
From chat discussion, it found that you have pivot field and for that you can change your belongsToMany relationship like this
class Benchmark extends Model
{
public function assessments()
{
return $this->belongsToMany(Assessment::class)->withPivot('comment','score')->withTimestamps();
}
}
class Assessment extends Model
{
public function benchmarks()
{
return $this->belongsToMany(Benchmark::class)->withPivot('comment','score')->withTimestamps();
}
}
Now fetch data
$assessment = Assessment::with(['benchmarks' => function($query){
$query->orderBy('content', 'desc');
}])->find($assessmentId);
In view you can render it like this
#foreach($assessment->benchmarks as $benchmark)
<tr>
<td>{{$benchmark->id}}</td>
<td>{{$benchmark->name}}</td>
<td>{{$benchmark->pivot->score}}</td>
<td>{{$benchmark->pivot->comment}}</td>
</tr>
#endforeach
For update you can use updateExistingPivot
For details check ManyToMany relationship https://laravel.com/docs/5.6/eloquent-relationships#many-to-many

Laravel Relation based on value within an JSON object

I have two models in which I need to relate to, a Users model and a Prices model. In my Prices model there is a JSON object which holds an ID of a user and I was wondering if I could relate to my Prices table using the ID which is in the Prices model?
I know you could use an getAttribute and then return the user like that, but I was wondering if there is a $this->hasOne() method you could use?
e.g.
JSON
{user_id: 1, other_values:"in the object"}
Prices Model
class Prices extends Model {
/* Prices has the column 'object' which has the JSON object above */
protected $casts = ['object' => 'array'];
public function user(){
return $this->hasOne("App\User", $this->object->user_id, "id"); /* ! Example ! */
}
}
I created a package with JSON relationships: https://github.com/staudenmeir/eloquent-json-relations
Since the foreign key is in the Prices model, you should use a BelongsTo relationship:
class Prices extends Model {
use \Staudenmeir\EloquentJsonRelations\HasJsonRelationships;
protected $casts = ['object' => 'array'];
public function user() {
return $this->belongsTo(User::class, 'object->user_id');
}
}
class User extends Model {
use \Staudenmeir\EloquentJsonRelations\HasJsonRelationships;
public function prices() {
return $this->hasMany(Prices::class, 'object->user_id');
}
}

How to apply relationship with two id's in laravel

So I have a model called data_storage and another model entity_states
I have to fetch the record from data_storage with entity_states where entity_state has data_storage_id and state_id.
How can I use eloquent to achieve this ?.
Or Ill have to use Query builder and use innerJoin?
Update1
My Actual Query
$this->values['new_leads'] = $data_storages->with('actions','states','sla')->where('wf_id',$wfid)->get();
My data_storage modal
class data_storages extends Model
{
//
protected $fillable = ['layout_id','member_id','company_id','team_id','data','status','wf_id'];
function actions()
{
return $this->hasMany('App\Models\ActionDataMaps', 'data_id', 'id' );
}
function states()
{
return $this->hasOne('App\Models\workflow_states','id','status');
}
function sla()
{
//Here I have to get those row from entity_states model where , data_storage_id and state_id
}
}
Thanks
Here's the more reasonable way to do it:
class DataStorage extends Model {
public states() {
return $this->belongsToMany(State::class,"entity_states");
}
}
class State extends Model {
public storages() {
return $this->belongsToMany(DataStorage::class,"entity_states");
}
}
Then you can eager-load related models via e.g.:
$storage = DataStorage::with("states")->first();
$storage->states->first()->column_in_related_state;
Or via the state:
$state = State::with("storages")->first();
$state->storages->first()->column_in_related_storage;
If there are additional columns in the pivot table entity_states then you can refer to them in the relationship as e.g.:
public states() {
return $this->belongsToMany(State::class)->withPivot("pivot_column");
}
In your model data_storage you can define a property / method entity_states to get them:
class data_storage extends Model
{
public function entity_states()
{
return $this->hasMany('App\entity_states','data_storage_id')->where('state_id ','=',$this->table());
}
}
Then you can access them in an instance by
$entityStatesOfDataStorage = $yourDataStorageInstance->entity_states;
See this link:
https://laravel.com/docs/5.3/eloquent-relationships
for Query Builder you may use this:
DB::table('data_storage')
->join('entity_states','data_storage.data_storage_id','=','entity_states.state_id')
->get();
For your reference Laravel Query Builder

Retrieving relationships of relationships using Eloquent in Laravel

I have a database with the following tables and relationships:
Advert 1-1 Car m-1 Model m-1 Brand
If I want to retrieve an Advert, I can simply use:
Advert::find(1);
If I want the details of the car, I could use:
Advert::find(1)->with('Car');
However, if I also want the detail of the Model (following the relationship with Car), what would the syntax be, the following doesn't work:
Advert::find(1)->with('Car')->with('Model');
Many thanks
It's in the official documentation under "Eager Loading"
Multiple relationships:
$books = Book::with('author', 'publisher')->get();
Nested relationships:
$books = Book::with('author.contacts')->get();
So for you:
Advert::with('Car.Model')->find(1);
First you need to create your relations,
<?php
class Advert extends Eloquent {
public function car()
{
return $this->belongsTo('Car');
}
}
class Car extends Eloquent {
public function model()
{
return $this->belongsTo('Model');
}
}
class Model extends Eloquent {
public function brand()
{
return $this->belongsTo('Brand');
}
public function cars()
{
return $this->hasMany('Car');
}
}
class Brand extends Eloquent {
public function models()
{
return $this->hasMany('Model');
}
}
Then you just have to access this way:
echo Advert::find(1)->car->model->brand->name;
But your table fields shoud be, because Laravel guess them that way:
id (for all tables)
car_id
model_id
brand_id
Or you'll have to specify them in the relationship.
Suppose you have 3 models region,city,hotels and to get all hotels with city and region then
Define relationship in them as follows:-
Hotel.php
class Hotel extends Model {
public function cities(){
return $this->hasMany(City::class);
}
public function city(){
return $this->belongsTo('App\City','city_id');
}
}
City.php
class City extends Model {
public function hotels(){
return $this->hasMany(Hotel::class);
}
public function regions(){
return $this->belongsTo('App\Region','region_id');
}
}
Region.php
class Region extends Model
{
public function cities(){
return $this->hasMany('App\City');
}
public function country(){
return $this->belongsTo('App\Country','country_id');
}
}
HotelController.php
public function getAllHotels(){
// get all hotes with city and region
$hotels = Hotel::with('city.regions')->get()->toArray();
}
will adding the relation function just ask for the relation needed
public function Car()
{
return $this->belongsTo(Car::class, 'car_id')->with('Model');
}
but if you want a nested relation just use the period in the with
Advert::with('Car.Model')->find(1);
but for multi-relation use the array
Advert::with('Car','Model')->find(1);

Categories