Laravel 5 Eloquent relationship: can't modify/overwrite relationship table property - php

I am using Laravel 5's belongsToMany method to define related tables using an intermediary pivot table. My application is using the eloquent models Tour and TourCategory. In the Tour Model I have:
namespace App;
use Illuminate\Database\Eloquent\Model;
class Tour extends Model
{
public function cats(){
return $this->belongsToMany('App\TourCategory', 'tour_cat_assignments', 'tour_id', 'cat_id');
}
}
In my controller I am retrieving all the data from the tour table along with the associated category data using Laravel's with method:
$tours = Tour::with('cats')->get();
That all works fine. The problem is that I don't want the category data in its current raw form, I need to first rearrange it. However I cannot overwrite the cats property without unsetting it first:
public function serveTourData(){
$tours = Tour::with('sections', 'cats')->get();
foreach($tours as $tour){
unset($tour->cats); // If I unset first, then it respects the new value. Why do I need to do this?
$tour->cats = "SOME NEW VALUE";
}
Log::info($tours);
}
Can someone explain the logic behind this please?

To override relations on some model, you can use:
public function serveTourData(){
$tours = Tour::with('sections', 'cats')->get();
foreach($tours as $tour){
$tour->setRelation('cats', "SOME NEW VALUE");
}
Log::info($tours);
}
For laravel 5.4 - setRelation
Of course if you are using laravel >= 5.6, you can unset relations by unsetRelation

Related

Eloquent eager loading does not return related model data

In my Laravel project I have two Eloquent Models Set and Card, they have one to many relationship between them. Set has many Cards and Cards belong to Set.
I'm trying to pull the data using Eloquent Eager Loading using the with() function. But for some reason the cards array inside sets is returning as blank [].
Set.php
<?php
namespace App;
use App\Card;
use Illuminate\Database\Eloquent\Model;
class Set extends Model
{
protected $table = "sets";
public function cards() {
return $this->hasMany('App\Card');
}
}
Card.php
<?php
namespace App;
use App\Set;
use Illuminate\Database\Eloquent\Model;
class Card extends Model
{
protected $table = "cards";
public function sets() {
return $this->belongsTo('App\Set');
}
}
Controller code
$sets = Set::with([
'cards' => function($query){
$query->select('name', 'description');
},
])->get();
return $sets;
name and description are the fields that I have in Cards table except of the primary key and foreign key.
It returns with "cards": [] in my JSON array of sets.
I have tried using below code in controller,
$sets = Set::with('cards.*')->get();
return $sets;
Did not work.
Tried adding the foreign key (set_id which is in the Cards table) in the hasMany method in my Set model that creates relationship with Cards table. But of no result.
To be mentioned, when I tried pulling the data from Cards table separately from my CardController, it returned all Cards data.
Some clue would mean great help. Thank you for your time.
Try this code in your controller,
$sets = Set::with('cards')->get();
return $sets;
Let me know if it works
Try something like this:
public function sets() {
return $this->belongsTo('App\Set','id','id_set');
}
the second parameter "id" is the id of table Set, and the third parameter "id_set" is the foreing key in the Card Table.

Laravel Eloquent one to many relationship not working

I'm obviously missing something. I thought I was comfortable around laravel relationships...
I've 2 tables, named ratings and ratingdetails. The models are named Rating & Ratingdetail:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Rating extends Model
{
public function ratingdetails()
{
return $this->hasMany('App\Ratingdetail');
}
public function campaigns()
{
return $this->hasMany('App\Campaign');
}
}
and
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Spatie\Translatable\HasTranslations;
class Ratingdetail extends Model
{
use HasTranslations;
public $translatable = ['value'];
public function rating()
{
return $this->belongsTo('App\Rating');
}
}
When I try to access to my Rating model it works fine, but I can't access the relationships; the output is the following, despite there should be 4 Ratingdetails rows...:
{"id":1,"description":"fontawesome","created_at":null,"updated_at":null,"deleted_at":null}
Thank you all for your time !
$rating = Rating::find($request->rating_id);
return $rating->toJson();
In the above line of code, you're never accessing the ratingdetails relationship. They are not included by default, and need to be loaded before being available:
$rating = Rating::with(["ratingdetails"])->find($request->rating_id);
return $rating->toJson();
Including it via with() will "Eager load" the relationship and expose it to be accessed via
console.log(rating.ratingdetails);
// Will contain an array of 4 objects
Before converting to json, you'd be able to access $rating->ratingdetails, but once converted, you lose access unless you have previously loaded the relationship.
Actually I can't answer for this question without having the Models' $fillable attributes, or without DB Tables structures. But I think your tables have following columns:
"raitings" -> "id", "description", "created_at", "updated_at", "deleted_at"
"raitingdetails" -> "id", "raiting_id", "value", ...
In normal way, you need to create OneToMany relation for that 2 tables with foreign key. So in your "raitingdetails" migration you need to have something like this:
$table->unsignedBigInteger('raiting_id')->nullable();
$table->foreign('raiting_id')->references('id')->on('raitings')->onUpdate('cascade')->onDelete('cascade');
Your models are correct, but it not just cool now.. You can improve them by adding $fillable columns and FKs of relations (Note: if you're using traditional foreign key concept, like "partents.id"->"childs.partent_id", then you can leave this part too).
For getting all Rating details of 1 Rating, you can do this:
$rating = Rating::find($rating_id);
$rating_details_of_one = $rating->ratingdetails()->get()->toJson();
If you want to have Rating Details for all actions, you can add Accessor in your Rating model and attach that to $appends like this:
protected $appends = [ 'rating_details' ]; public function
public function getRatingDetailsAttribute() {
return $this->ratingdetails;
}
And in logic parts you can access like this:
$ratings = Rating::find($rating_id); // this will get with their "ratingdetails" relation
Or you can attach accessor on the fly without protected $appends and getRatingDetailsAttribute() function like this:
$rating = Rating::find($rating_id);
$rating_details_of_one = $rating->setAppends([ 'rating_details' ])->get()->toJSON();
If you want to have some Ratings with their details, you can use something like this:
$rating_details_of_many = Rating::where('description', 'fontawesome')->with('ratingdetails')->get()->toJson();

Why I can't retrieve data using a one-to-many relationship in laravel 5.5

I am trying to return data from my database and I want it to include data from the related table. It is a one-to-many relationship. However I all I get is an error
Property [User] does not exist on this collection instance.
In my User model I have
//App\User.php
class User extends Authenticatable{
use Notifiable;
public function systems(){
return $this->hasMany(\App\Models\General\systems::class,'added_by','id');
}
The other model, called systems I have
//App\Models\General\systems.php
class systems extends Model
{
public function User(){
return $this->belongsTo(\App\User::class,'added_by','id');
}
In my controller I have
$this->systemsObject = new \App\Model\General\systems();
$systems = $this->systemsObject->get()->User;
according to the Laravel Documentation this should work but it isn't. I tried reversing the foreign key/local key parameters. I made the ->User uppercase, lowercase.
I have no idea what I am doing wrong
You need to iterate over the collection, for example:
$systems = $this->systemsObject->get();
foreach ($systems as $system) {
echo $system->User->name;
}

Pass variable to laravel method from within the same model

I am working on some multi tenancy updates to a Laravel app but hitting an issue when trying to pass a specific team ID into a method on a model from within another method.
Example:
In Controller:
$waitTime = $booking->estimatedWaitTime($teamId);
In Booking model:
public function queueLength()
{
$booking = $this->todaysBookings;
foreach ($bookings as $booking) {
// Calculate the length of all bookings
}
}
public function todaysBookings()
{
return $this->hasMany('App\UserBooking')->whereHas('booking', function ($q) {
$q->where('team_id', 2);
});
}
This correctly returns the bookings and allows me to loop through them in the queueLength method. However, I want to be able to pass the team_id into the todaysBooking method.
When instead calling todaysBookings as a method:
$booking = $this->todaysBookings();
It doesn't return anything for me to loop through.
Any ideas how to achieve what I want to do here?
You can pass the id as parameter and then get the results :
public function todaysBookings($team_id)
{
return $this->hasMany('App\UserBooking')->whereHas('booking', function ($q) use($team_id) {
$q->where('team_id', $team_id);
});
}
In the call you can do as follow :
$some_teame_id = 2;
$booking = $this->todaysBookings($some_teame_id)->get();
Why ?
Because the relationships in laravel serve as powerful query builders (documentation) :
Eloquent relationships are defined as methods on your Eloquent model
classes. Since, like Eloquent models themselves, relationships also
serve as powerful query builders, defining relationships as methods
provides powerful method chaining and querying capabilities.
Relationship Methods Vs. Dynamic Properties :
The call of the relationship as Methods is needed when you want to add some more conditions and filters like this :
$booking->todaysBookings()->where('active', 1)->get(); //just an example :)
And if you do not need to add additional constraints to an Eloquent relationship query, you may simply access the relationship as if it were a property, Laravel will add the get() for you :)
$booking->todaysBookings

getting the value of an extra pivot table column laravel

I have a phone_models, phone_problems, and a phone_model_phone_problem pivot table. The pivot table has an extra column 'price'.
PhoneModel:
class PhoneModel extends \Eloquent
{
public function problems()
{
return $this->belongsToMany('RL\Phones\Entities\PhoneProblem')->withPivot('price');
}
}
PhoneProblem:
class PhoneProblem extends \Eloquent
{
public function models()
{
return $this->belongsToMany('PhoneModel')->withPivot('price');
}
}
What I'm trying to do is get the price of a specific phone with a specific problem.
This is how I have it now but I feel like Laravel has a built in Eloquent feature I can't find to do this in a much simpler way:
$model = $this->phoneService->getModelFromSlug($model_slug);
$problem = $this->phoneService->getProblemFromSlug($problem_slug);
all this does is select the specific model and problem from their slug.
then what I do is with those credentials I get the price like so:
$row = DB::table('phone_model_phone_problem')
->where('phone_model_id', '=', $model->id)
->where('phone_problem', '=', $problem->id)
->first();
so now I can get the price like so $row->price but I feel like there needs to be a much easier and more 'Laravel' way to do this.
When using Many to Many relationships with Eloquent, the resulting model automatically gets a pivot attribute assigned. Through that attribute you're able to access pivot table columns.
Although by default there are only the keys in the pivot object. To get your columns in there too, you need to specify them when defining the relationship:
return $this->belongsToMany('Role')->withPivot('foo', 'bar');
Official Docs
If you need more help the task of configuring the relationships with Eloquent, let me know.
Edit
To query the price do this
$model->problems()->where('phone_problem', $problem->id)->first()->pivot->price
To get data from pivot table:
$price = $model->problems()->findOrFail($problem->id, ['phone_problem'])->pivot->price;
Or if you have many records with different price:
$price = $model->problems()->where('phone_problem', $problem->id)->firstOrFail()->pivot->price;
In addition.
To update data in the pivot you can go NEW WAY:
$model->problems()->sync([$problemId => [ 'price' => $newPrice] ], false);
Where the 2nd param is set to false meaning that you don't detach all the other related models.
Or, go old way
$model->problems()->updateExistingPivot($problemId, ['price' => $newPrice]);
And remind you:
To delete:
$model->problems()->detach($problemId);
To create new:
$model->problems()->attach($problemId, ['price' => 22]);
It has been tested and proved working in Laravel 5.1 Read more.
Laravel 5.8~
If you want to make a custom pivot model, you can do this:
Account.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Account extends Model
{
public function users()
{
return $this->belongsToMany(User::class)
->using(AccountUserPivot::class)
->withPivot(
'status',
'status_updated_at',
'status_updated_by',
'role'
);
}
}
AccountUserPivot.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Relations\Pivot;
class AccountUserPivot extends Pivot
{
protected $appends = [
'status_updated_by_nice',
];
public function getStatusUpdatedByNiceAttribute()
{
$user = User::find($this->status_updated_by);
if (!$user) return 'n/a';
return $user->name;
}
}
In the above example, Account is your normal model, and you have $account->users which has the account_user join table with standard columns account_id and user_id.
If you make a custom pivot model, you can add attributes and mutators onto the relationship's columns. In the above example, once you make the AccountUserPivot model, you instruct your Account model to use it via ->using(AccountUserPivot::class).
Then you can access everything shown in the other answers here, but you can also access the example attribute via $account->user[0]->pivot->status_updated_by_nice (assuming that status_updated_by is a foreign key to an ID in the users table).
For more docs, see https://laravel.com/docs/5.8/eloquent-relationships (and I recommend press CTRL+F and search for "pivot")

Categories