I have model House with relation hasMany HousePhotos.
I try get link to main photo from table house_photos.
class House extends Model
{
public function photos(){
return $this->hasMany('app\HousePhoto');
}
public function get_main_photo(){
return $this->photos()->where('main', true);
}
}
Controller:
$house=House::find(1);
In View i use
{{$house->main_photo()->link}}
and got error.
When i use
{{$house->main_photo()}}
i got object. How to get string value of link to photo?
First of all you need to understand the difference between the Builder object and the Collection object.
As it is now, function get_main_photo returns a Builder object (actually a Relation object, which contains a Builder).
On these objects you can call function get() to finish the query and get the results. In this case you will get a Collection object, which you will be able to iterate.
Alternatively, in this case you seem to only have one 'main photo' per house, so instead you can call first():
public function get_main_photo(){
return $this->photos()->where('main', true)->first();
}
This will return the single associated model, on which you will be able to access ->link (if the model was found).
Remember at any point while debugging you can call the convenient dd function (which dumps the object passed as parameter and terminates the applicacion) to see what type of object you are dealing with, and all its attributes.
Related
So I'm trying to add get a specific data from a related table using the below method, but I don't know if that is the correct way to do it. here is what it looks like.
public function transspecific($lid){
return $this->belongsTo('raplet\Keepertrans')->where("lang_id", $lid);
}
and then I try to get data from it
dd($akeeper->transspecific($akeeper->id));
it doesn't act like there is anything but when I type dd("hello") inside the model, it works. so clearly I have something wrong with my relationship context
What are you are trying to do is adding a [dynamic scope in laravel][1] model, which is totally fine. Except you need to declare the scope seperated from relationship method.
Relationship:
public function keepertrans(){
return $this->belongsTo('raplet\Keepertrans');
}
Scoped:
public function transspecific($lid){
return $this->keepertrans()->where("lang_id", $lid);
}
Then you can call the scope with a get() to execute the query builder:
(SomeOtherRelatedModel::first())->transspecific($someId)->get();
The methods available in Eloquent model for relationship are different than what you need. Whenever you need to add a custom function which internally adds some filters to your query (builder), you need to use scopes
The generic rule of scope function is scope + yourfunction
In your case you will need to create scopeTranspecific function.
Each scope gets the first argument as builder which you update inside the function. The later arguments are optional.
public function scopeTranspecific($query, $lid){
return $query->keepertrans()->where("lang_id", $lid);
}
And then you can use it :
Model::where('column1' , 'value')->transpecific($id)->get()
If you just dump it without ->get() you will get the query builder instance. You will have to do ->get() to get the data
I have a user model, and I want to check if a user has been assigned a mentor.
public function mentorapplication()
{
return $this->hasMany(mentorApplication::class, 'user_id');
}
public function mentorAssigned()
{
return ($this->mentorapplication()->status == "counsellorAssigned");
}
when I call $user->mentorAssigned() in tinker I get an undefined property for status.
Any ideas?
A couple of things here. Firstly, your relationship is has-many, which is one-to-many. When you try to use the relationship you should expect a Collection to be returned, rather than a single entity. Therefore trying to access the ->status property on returned collection won't work.
Secondly, when you're trying to access the relationship like this:
$this->mentorapplication()
You will get a HasMany instance, which would allow you to chain on more constraints to the query, just like ->where(...), or ->orderBy(...). You would need to use something like ->get() or ->first() to actually run the query and get the results you're after. You can omit the () here and Laravel will load the relation for you, returning your collection:
$this->mentorapplication
Now, I don't know exactly what it is you're trying to achieve, but say for instance you wanted to see if at least one of the mentorApplication objects assigned to the user has a status of counsellorAssigned, you could do something like this:
public function mentorAssigned()
{
foreach ($this->mentorapplication as $mentorapplication) {
if ($mentorapplication->status == 'counsellorAssigned') {
return true;
}
}
return false;
}
Or you could use Laravel's Collection methods to help you rather than just doing a loop here. There are many approaches you could take so I will just leave it there.
I have a table 'tour2s' with 2 rows and when I do:
$tour = Tour2::find(1);
dd($tour);
it returns the tour with 'id' = 1. And it's Object.
I want to turn the object to collection of only attributes of the model, nothing else. And I know that when I use ->get() it returns collection.
But when I am trying:
$tour = Tour2::find(1)->get();
dd($tour);
It returns a collection but of all 2 tour objects (full objects, not only attributes):
I did it like:
$tour = Tour2::find(1);
$tour = collect($tour);
dd($tour);
and now it's what i what - it return a collection of only model attributes (WHAT I WANTED):
SO, my question is why when I used $tour=Tour2::find(1)->get() it returned all tours not only the one with 'id'=1 ?
Passing an array to find() will return a collection.
$tour = Tour2::find([1]);
However, it will be a collection of Tour2 objects, not only the attributes.
Then, if you want only the attributes, you could use $tour->toArray()
You could also do $tour = collect(Tour2::find(1));
And to answer your question, when you use $tour=Tour2::find(1)->get(), Laravel fetch the first tour, and then calling get() on $tour will fetch all other records, so return two tours in your case.
Ok, the main question, as i understand is: "Why when i wrote Tour2::find(1)->get() i receives collection of all records".
when you wrote Tour2::find(1) it assumes that you receive instanse of model Tour2. So we can simple write $tourInstanse->get()
If you go to \Illuminate\Database\Eloquent\Model you can see that here is no method called get() but we have a magic method __call. Look at his implementation:
public function __call($method, $parameters)
{
if (in_array($method, ['increment', 'decrement'])) {
return $this->$method(...$parameters);
}
return $this->newQuery()->$method(...$parameters);
}
So, when you call get() method on a model instance you get model`s QueryBuilder (as described in last row) and call get() method on a QueryBuilder. As a result, you receiving all records of that model Class.
I have a Model House and a Model energy_class.
A house has only one energy class. Energy Classes can be assigned to multiple houses. I therefore defined the relationships like this:
House.php
class House extends Model
{
public function energy_class()
{
return $this->hasOne('App\energy_class', 'id', 'energy_class');
}
}
energy_class.php
class energy_class extends Model
{
public function house()
{
return $this->belongsToMany('App\House');
}
}
When passing the House Data to a view like this:
$house = House::with('energy_class')->find($id);
return view('admin.houses.edit')->with('house', $house);
And referencing it on the view like this:
$house->energy_class()->name
I get this error: Undefined property: Illuminate\Database\Eloquent\Relations\HasOne::$name
When doing it like this: $house->energy_class->name I get trying to get property of non-object
Your first suggestion:
$house->energy_class()->name
Does not work, because at this point you only called the relationship, you didn't yet specify from it what you wanted, e.g.:
$house->energy_class()->first()
Your second try returns empty because $house->energy_class (which does the same as the above code), didn't return any results.
There can be various causes for this:
Your relationship is not properly signed up
Your database is not properly setup (have you set up a foreign key in your migration?)
The house actually does not have an energy class filled out
I suspect in your case it might be the second or the third. Could you paste me your migration script?
Edit: PS. if this error occurs because perhaps not each house actually has a an energy class, you could use the following code to check for this:
$house->energy_class ? $house->energy_class->name : "No energy class available";
class Batch extends Eloquent {
public function coupons() {
return $this->hasMany('Coupon');
}
}
class Coupon extends Eloquent {
public function batch() {
return $this->belongsTo('Batch');
}
public function price() {
$batch = $this->batch;
return $batch->price;
}
}
$coupon->price gives me this error:-
LogicException Relationship method must return an object of type
Illuminate\Database\Eloquent\Relations\Relation
However, $coupon->batch->price works just fine.
What am I missing?
Your issue here is that you define a non-relationship method price() but you call it as if it was a relationship method (i.e. you call it as a property and not as a method).
The code you should be using to get the Coupon's price is:
$coupon->price();
The reason the relationship thing works (i.e. $coupon->batch over $coupon->batch()) is that Laravel has some special logic in - basically it catches you trying to access a property (->batch in this case) and checked to see if there's a corresponding method (->batch()). If there is one, it calls it and expects it to return a relationship, and then it calls ->first() of ->get() on that relationship depending on whether it's a single-result or a multiple-result relationship.
So what's happening in your code here is that you call $coupon->price and Laravel, behind the scenes, decides that being as there's a ->price() method it must be a relationship. It calls the method, checks that it returns one of the Laravel relationship types, and when it doesn't, throws that LogicException.
The general rules of thumb is this:
If you want an actual property (i.e. anything defined on the table) or the results of a relationship query, use property access
If you want anything else (i.e. behaviour you're defined using a method) you must call the method directly
Also, sometimes there is a good reason to call a relationship as the method rather than the property - calling the method returns something you can add query builder constraints on to, whereas calling the property gets you all the results. So say Coupons can be enabled or disabled (for example), the following holds:
$batch->coupons gets you all coupons that the batch has
$batch->coupons()->whereEnabled(1)->get() gets you all enabled coupons for a given batch
$batch->coupons()->orderBy('order')->get() gets you all the coupons that the batch has, ordered by a field called order
$coupon->batch gets you the given coupon's batch
Hopefully that explains the difference between Eloquent's use of methods versus properties for relationships, and why all augmented behaviour (like price on coupon in your example, but not price on batch, which is inherent behaviour) must be called as a method.
Take a moment to realize what objects you actually have here.
When you call $this->batch; you're no longer chaining the relationship queries - you've actually retrieved the information from the database already. In order to define that query you'd have to do it one of several ways including:
Coupon::with('batch.price')->get();
You could of course do it with relationships but it's late and I'm not sure where exactly Price belongs in the scheme of this since I don't see a method for it associated with batch. Presumably you could do:
public function price()
{
return $this->batch->price;
}
if Price is a derivative of Batch.