Custom model method to get a relation in Laravel - php

I try to define a custom Model method in Laravel. I have a n:m relation between Subscription and Notification over SubscriptionNotification.
I already defined the default relations:
public function subscription_notifications() {
return $this->hasMany('App\SubscriptionNotification');
}
public function notifications() {
return $this->belongsToMany('App\Notification', 'subscription_notifications');
}
Now I want to define a method, which returns a collection of notifications. I collect the IDs of the notifications I want in an array and write the following method:
public function notifications_due() {
// Collect $notification_ids
return $this->belongsToMany('App\Notification', 'subscription_notifications')->whereIn('notifications.id', $notification_ids)->get();
}
But when I want to use the mothod by $subscription->notifications_due, I get the following error:
[LogicException]
Relationship method must return an object of type Illuminate\Database\Eloquent\Relations\Relation
I'm new to Laravel (I come from Rails). I don't know if this is in Laravel even possible. Maybe someone can help me. Thanks!

Remove the ->get() part in the method notifications_due. get() will return a Collection, but when calling the method as a property (or magic method), Laravel expects the method to return an instance of Relation. Laravel will then execute the query and transform it to a Collection automatically.
Also, you can use your already defined notifications() method:
public function notifications_due() {
// Collect $notification_ids
return $this->notifications()->whereIn('id', $notification_ids);
}

Remove the get call from your relationship method, for example:
public function notifications_due() {
return $this->belongsToMany(
'App\Notification',
'subscription_notifications
')->whereIn('notifications.id', $notification_ids);
}
Use it just same:
// It'll return a collection
$dues = $subscription->notifications_due;
To get all the ids from the collection you may try this:
$ids = $dues->pluck('id');
Also, you may add more constraints if you want if you use it like:the
$dues = $subscription->notifications_due()->where('some', 'thing')->get();
Or paginate:
$dues = $subscription->notifications_due()->where('some', 'thing')->paginate(10);

Related

get field value from eloquent relationship

I have this relationship on eloquent
public function Manufacturer() {
return $this->hasOne('App\Models\ManufacturerModel', 'id')->select('name');
}
And this returns correctly the manufacturer name:
{"id":1,"serialnumber":"123_1","buydate":"2018-01-26 00:00:00","offservice":null,"deleted":"0","manufacturer":{"name":"HP"}}
I want to retrieve the name not as JSON object but as a string
{"id":1,"serialnumber":"123_1","buydate":"2018-01-26 00:00:00","offservice":null,"deleted":"0","manufacturer":"HP"}
The best way to define the relationship is:
public function Manufacturer() {
return $this->hasOne('App\Models\ManufacturerModel', 'id');
}
Then you can get the manufacturer name this way:
$your_object->manufacturer->name;
Or adding a wrapper method:
public function ManufacturerName() {
return $this->manufacturer->name;
}
Notice that when you refer to the relationship without parenthesis the query is executed and what you are accessing is the result. If you don't want the entire record to be queried you can do this:
public function ManufacturerName() {
return $this->manufacturer()->select('name')->get()->name;
}
By accessing the relationship with parenthesis you are getting the relationship definition and you can modify it before executing the query.
Not directly but you can achieve the functionality using a little bit trick of php, for example, if you would like to use it in string context as given below:
// {{ $someModel->manufacturer }}
echo $someModel->manufacturer; // or echo Manufacturer::find(1);
Then you can do it using the __toString magic method in Manufacturer model as given below:
public function __toString()
{
return $this->name;
}
In this case, even on json_encode($manufacturer) will give you just name so why don't you just use $model->manufacturer->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

Return collection instead of relationship in Eloquent Model

In my Model, I have a collection and I am trying to return it in my model, instead of relationship object.
So that I can call, $user->items and get that collection.
In the model, the function looks like:
class User extends Model {
public function channelsAttribute() {
$name = Company::where('id', $this->id)->first()
$items = Item::where('company_id', $name)->get();
return $items;
}
}
Relationship method must return an object of type Illuminate\Database\Eloquent\Relations\Relation
As it goes 2 ways, I couldn't find a way to use relationship; however $items returns collection of values.
What should I do?
You are not calling the method correctly, instead of
function channelsAttribute()
you need to do
function getChannelsAttribute()

Use accessor mutator's within Laravel 4 lists method

I have a simple mutator:
public function getFullnameAttribute($value)
{
return $this->first_name. ' ' .$this->last_name;
}
But I also have a method that returns a list of specific users for use in a select input:
static function getDeliveryManagers()
{
return User::IsDeliveryManager()->lists('first_name', 'id');
}
I need that array returned to be ['First Name Last Name', 'id']
But the mutator doesn't work, e.g.
return User::IsDeliveryManager()->lists('Fullname', 'id');
I found this: https://github.com/laravel/framework/issues/668
Which Taylor marks as being done but I cant get it working. Using Laravel 4.2.
Any help?
You need to call the lists() on Eloquent Collection. So make sure IsDeliveryManager() is returning correct values. Its better if you can share the code of IsDeliveryManager(). If its is query scope you need to call get() before lists().
return User::IsDeliveryManager()->get()->lists('fullname', 'id');
The mutator, even if it is in camelcase, should be called in lowercase.
So you should be calling it as such:
return User::IsDeliveryManager()->lists('fullname', 'id');

Model relations not working when using queryBuilder methods in Laravel

How can I use Laravel Relations while using Query Builders ?
$Cars = User::find(1)->car; // works well and returns all cars
$User = User::where('username' , 'John')->get();
$Cars = $User->car; // doesn't work.
Any help.
thanks
Solved
$User = User::where('username' , 'John')->get();
Should Be
$User = User::where('username' , 'John')->first(); // to get an object instead of array
The ::find() method will return an instance of your model, which allows you to access relations and such that are setup on the model.
The ->get() method will return an Illuminate\Database\Eloquent\Collection of results, even if there is only 1 row selected from the database.
Using the collection you can then loop through the results
$collection->each(function($model)
{
// You can now access $model->cars
});
Alternativly you may use the ->first(), ->last() and ::find() methods to return a single instance of your model which allows you to directly access relations.
$model = Model::where('foo', 'bar')->first(); // Returns first row from the database
$model->cars // Is now accessable
Assuming you already have your Cars class all you have to do is to add method to your User class.
//add this method to your User class
public function cars()
{
//since you'll be using many to many
//you might use `user_cars` as pivotal table
return $this->belongsToMany('Cars', 'user_cars', 'cars', 'cars');
}
You may call it using:
User::find(1)->cars()->get();

Categories