In my Room object I want to get an Picture with the lowest priority. So to my Room model i've added:
public function picture(){
Return Picture::where('room_id', $this->id)->orderBy('priority', 'asc')->first();
}
In my controller I call this method like:
public function($id){
$room = Room::findOrFail($id);
$room->picture();
}
But when i try to get it in my view like:
{{$room->picture}}
I get the following error:
Relationship method must return an object of type Illuminate\Database\Eloquent\Relations\Relation
When using {{$room}} I dont see any picture object in the room object but the app doesnt crash.
If you want to be able to fetch the latest picture like that, you'll need to put relation definition in your pictures() method instead of fetching the object. This way you'll be able to make use of Eloquent's eager loading; fetching the picture with the lowest (in terms of value) priority will also be very easy.
Add the following to your Room class:
//relation definition - one to many
public function pictures() {
return $this->hasMany(Picture::class);
}
//Eloquent getter
public function getPictureAttribute() {
return $this->pictures()->orderBy('priority', 'asc')->first();
}
Now, you can easily access the most important picture on a $room object by doing:
$picture = $room->picture;
You can read more about how to set up different types of relations in your models here: http://laravel.com/docs/5.1/eloquent-relationships
Related
I'm currently struggling with retrieving data towards a parent model. I'll drop my database, classes, and things I've tried before.
I have 4 tables: sales_orders, products, work_orders, and product_sales_order (pivot table between sales_orders and products).
SalesOrder.php
class SalesOrder extends Model
{
public function products()
{
return $this->belongsToMany(Product::class)
->using(ProductSalesOrder::class)
->withPivot(['qty', 'price']);
}
}
ProductSalesOrder.php
class ProductSalesOrder extends Pivot
{
public function work_orders()
{
return $this->hasMany(WorkOrder::class);
}
public function getSubTotalAttribute()
{
return $this->qty* $this->price;
}
}
WorkOrder.php
class WorkOrder extends Model
{
public function product_sales_order()
{
return $this->belongsTo(ProductSalesOrder::class);
}
public function sales_order()
{
return $this->hasManyThrough(
ProductSalesOrder::class,
SalesOrder::class
);
}
}
So, what I want to retrieve sales order data from work order since both tables don't have direct relationship and have to go through pivot table and that is product sales order. I've tried hasOneThrough and hasManyThrough but it cast an error unknown column. I understand that error and not possible to use that eloquent function.
Is it possible to retrieve that sales order data using eloquent function from WorkOrder.php ?
You cannot achieve what you want using hasOneThrough as it goes from a table that has no ID related to the intermediate model.
In your example you are doing "the inverse" of hasOneThrough, as you are going from a model that has the ID of the intermediate model in itself, and the intermediate model has the ID of your final model. The documentation shows clearly that hasOneThrough is used exactly for the inverse.
So you still should be able to fix this, and use a normal relation as you have the sales_orders_id in your model SuratPerintahKerja, so you can use a normal relation like belongsTo to get just one SalesOrder and define it like this:
public function salesOrder()
{
return $this->belongsTo(SalesOrder::class, 'sale_orders_id');
}
If you want to get many SalesOrders (if that makes sense for your logic), then you should just run a simple query like:
public function salesOrders()
{
return $this->query()
->where('sale_orders_id', $this->sale_orders_id)
->get();
}
Have in mind that:
I have renamed your method from sales_order to salesOrder (follow camel case as that is the Laravel standard...).
I have renamed your method from sales_order to salesOrders for the second code as it will return more than 1, hence a collection, but the first one just works with one model at a time.
I see you use sale_orders_id, but it should be sales_order_id, have that in mind, because any relation will try to use sales_order_id instead of sale_orders_id, again, stick to the standards... (this is why the first code needs more parameters instead of just the model).
All pivot tables would still need to have id as primary and auto incremental, instead of having the id of each related model as primary... Because in SuratPerintahKerja you want to reference the pivot table ProdukSalesOrder but it has to use both produks_id (should have been produk_id singular) and sale_orders_id (should have been sales_order_id). So if you were able to use something like produk_sales_order_id, you could be able to have better references for relations.
You can see that I am using $this->query(), I am just doing this to only return a new query and not use anything it has as filters on itself. I you still want to use current filters (like where and stuff), remove ->query() and directly use the first where. If you also want to add ->where('produks_id', $this->produks_id) that is valid and doesn't matter the order. But if you do so, I am not sure if you would get just one result, so ->get() makes no sense, it should be ->first() and also the method's name should be salesOrder.
Sorry for this 6 tip/step, but super personal recommendation, always write code in English and do not write both languages at the same time like produks and sales orders, stick to one language, preferrably English as everyone will understand it out of the box. I had to translate some things so I can understand what is the purpose of each table.
If you have any questions or some of my code does not work, please tell me in the comments of this answer so I can help you work it out.
Edit:
After you have followed my steps and changed everything to English and modified the database, this is my new code:
First, edit ProductSalesOrder and add this method:
public function sales_order()
{
return $this->belongsTo(SalesOrder::class);
}
This will allow us to use relations of relations.
Then, have WorkOrder as my code:
public function sales_order()
{
return $this->query()->with('product_sales_order.sales_order')->first();
}
first should get you a ProductSalesOrder, but then you can access ->sales_order and that will be a model.
Remember that if any of this does not work, change all the names to camelCase instead of kebab_case.
i'm using laravel 7.30, I have a post model linked to user model with belongsTo relationship, I want to retrieve all posts with user property that contains only the name of the user for api purposes.
what i've tried so far.
public function index()
{
return Post::with('user:id,name')->get();
}
but the result of this code is a nested object 'user' which has 2 fields id and name on each post, I only want a single field with posts fields called user which has the name only and not a nested object for api purposes.
I've made it using database query builder, but i'm looking for a way using Eloquent
Thanks in advance
You can add this to your Post Model:
protected $appends = [
"author_name"
];
public function getAuthorNameAttribute()
{
return $this->user->name;
}
This will make sure the user's name is appended to the Post object every time it is being called.
Then from your controller, you can easily do this:
public function index()
{
return Post::get();
}
In my Profile model I setted this relationship
public function lease()
{
return $this->belongsTo(Lease::class, 'lease_id', 'id');
}
And in my Lease model I seeted this way
public function profile()
{
return $this->hasOne(Profile::class, 'lease_id', id);
}
As longs as I know in laravel you could do
$profile = factory(App\Profile::class)->create();
$profile->lease()->get();
And then responds correctly with the model inside of a collection
And if I do
$profile->lease
Responds correctly directly with the model
It isn't supposed that dynamic propertis execute the query right away like a shortcut of ->lease()->get()? Why it gives different formatted results?
When you are calling get on a builder you are getting a collection always. When you call first on a builder like that you will get a model or null. The dynamic property for the relationship, based upon the relationship object, will either query with get or first respectively when it loads it. Which is why $model->relationship is returning you the result you expect.
The relationships that are singular, cause a find and the ones that are many cause a get.
Laravel 5.4 - Docs - Eloquent - Relations - Relationship Methods vs Dynamic Properties
Here is model structure of my Laravel 5.3 project,
User.php (Model)
it has one invitation method that returns the invitation of a user.
public function invitations()
{
return $this->hasMany( 'App\Invitation', 'invitee_id', 'id' );
}
Invitation.php (Model)
This model has another method that would return the inviter detail of an invitation.
public function inviter()
{
return $this->hasOne( 'App\User', 'id', 'invited_by' );
}
If i want to retrieve all invitations of current user it works,
\Auth::user()->invitations;
But if i try to get the information about the inviter it won't work! (Question: How to do it?)
\Auth::user()->invitations->inviter;
Though i can query the inviter from a invitation eloquent object like this,
\App\Invitation::first()->inviter;
But this is not working when i try to access it from the user model -> invitation -> inviter!
Also can i use eager loading here?
\Auth::user()->invitations->inviter;
Looking at this, it appears that you're attempting to retrieve the inviter property from a collection of invitations. The reason Ken's suggestion to use \App\Invitation::first()->inviter; worked is because you are retrieving the inviter of only one invitation (in this instance, the first). To resolve this, loop through your invites before attempting to retrieve the properties for each one:
$invitations = \Auth::user()->invitations;
foreach ($invitations as $invitation) {
$inviter = $invitation->inviter;
}
There is also an each() method specific to Laravel Collections that will allow you to loop through your object.
I have 3 tables / models
User (has an id)
Articles (has an id )
UserArticles (has an id, article_id and user_id)
I am a little confused on how I would set up the relationship so that I will be able to get all articles connected to a user so I can set up a call like so in my controller:
$articles = User::find(Auth::user()->id)->articles->paginate(20);
I figured this was a manytomany relationship so I am playing around with this code inside the User model:
public function articles()
{
return $this->belongsToMany('App\Article', 'user_saved_articles', 'user_id', 'article_id');
}
Now this works, as long as I don't call paginate() on the controller function I'd like to use above. Here is where my real issue lies now, so it works with
$articles = User::find(Auth::user()->id)->articles;
but with this:
$articles = User::find(Auth::user()->id)->articles->paginate(20);
it comes up with the error:
FatalErrorException in UserController.php line 217:
Call to undefined method Illuminate\Database\Eloquent\Collection::paginate()
I can't figure out why I can't paginate on this, as I can with all my other queries.
If you call an eloquent relation as an attribute User::find(Auth::user()->id)->articles it will automatically execute the SQL, and return a Collection. The error is telling you that you have a Collection, which you can't call paginate() on.
If you want to reference the relationship to add more statements you need to call it as a function User::find(Auth::user()->id)->articles(). This will return a QueryBuilder instance that you can add statements to, and/or paginate.
So this should work:
$articles = User::find(Auth::user()->id)->articles()->paginate(20);