Laravel. Get data from pivot table - php

How can i get data from pivot table directly (with Laravel)?
I have model \App\Model\Orders
class Orders extends Model
{
public function related_orders()
{
return $this->belongsToMany(\App\Model\Orders::class,'related_orders','order_id','related_id');
}
}
oK, i can get data from pivot table as
$order = \App\Model\Orders::find(1);
foreach ($order->related_orders as $related_order) {
$related_order->pivot->related_id;
}
But, i need only related order id! I don't want retrieve all related orders for get one id field. It's irrationally.
Can you help me?
PS: I know how do it with sql query it's no problem. I wonder how to do this with Laravel.

What you want to do seems pretty simple if I'm understanding correctly. If you want an array of ID's for the related orders, just do this:
$relatedOrders = $order->related_orders->pluck('id');
Let me know if that's what you meant and if that gives the correct result, perhaps there's something more you're looking to do that I didn't understand.

You can try this:
Get a specific order (in this case, 1):
$order = \App\Model\Orders::find(1);
Get the related order for the above order:
$relatedOrders = $order->related_orders->first();
Get the ID (assuming your column is id)
return $relatedOrders->id;

Related

Retrieve Parent Model Through Pivot Table Laravel

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.

Laravel Eloquent query unexpected result

I've found some query result really unexpected.
It's Laravel 5.2
We have following entity:
User with method:
public function roles() : BelongsToMany
{
return $this->belongsToMany(Role::class)->withPivot('timestamp');
}
Each User can have many roles, so we have also Role entity (but it doesn't matter much in my question) and pivot table user_role with timestamp field (and ids of course), because we hold information about time, when User achieved specific role.
I want to get all Users with theirs last assigned Role
When I create query (in User context in some repository):
$this->with(['roles' => function($query) {
$query->orderBy('timestamp', 'desc');
}])->all();
the result will contain Users with Roles entities inside itself ordered by timestamp - it's ok. But I want to retrieve only one last role inside each User entity not all ordered.
So...
$this->with(['roles' => function($query) {
$query->orderBy('timestamp', 'desc')->limit(1);
}])->all();
And then I retrieve Users but only User which achieved some Role for the very last time contains it! All the other Users have their roles field containing empty array.
Why ordering was performed on each Users relation separately, but when I added limit it behaved like a global limit for all.
It drives me crazy...
Thanks for advices.
EDIT
I've created lastRoles() method to get all Roles ordered desc. But all, retrieving one is impossible.
public function lastRoles() : BelongsToMany
{
return $this->BelongsToMany(Roles::class)->withPivot('timestamp')->latest('timestamp');
}
And for testing:
$users = (new User())->with('lastRoles')->get();
But now I must iterate over Users and invoke lastRoles() on each one:
foreach ($users as $user) {
var_dump($user->lastRoles()->get()->first()->name);
}
Then I retrieve names of latest Roles assigned to each User.
So... There is no way to do it in one query? This is the only way?
For this to work, you would need a helper function:
public function latestRole()
{
return $this->hasOne(Role::class)->withPivot('timestamp')->orderBy('timestamp', 'DESC');
}
And then:
$this->with('latestRole')->get();
Credits to this awesome article.
When you eager load a relationship with query constraint(s), the query will be run once to load all relationships, not each one individually. This is the expected behavior. Think about it, eager loading exists to turn many queries into one query in order to optimize performance. There is only one query executed, so your limit constraint will limit the entire result set, rather than on a per model basis.
To circumvent this, you could try creating another belongsToMany method that adds the desired limit constraint. The following code is untested:
public function lastRole() : BelongstoMany
{
return $this->belongsToMany(Role::class)
->withPivot('timestamp')
->orderBy('timestamp', 'desc')
->limit(1);
}
Assuming this works, you can then simply change the relationship method from roles to lastRole and remove your query constraint:
$this->with('lastRole')->all();

laravel update and select

I have a situation to update the row based on id and return the complete row. I can do it in two queries but i want to do it in one query only. I have write this...
$result= DB::table($this->table)
->whereRaw($where['rawQuery'], $where['bindParams'] ? $where['bindParams'] : array())
->increment($updateField);
if($result){
return DB::table($updateTable)
->select('id')
->where('campaign_id',$where['bindParams'])
->where('date',date("Y-m-d"))
->get();
}else{
throw Exception("Error in fetching data");
}
I copied this from what you commented in your question:
No i want to return the id of the updated row or complete row if possible
If you want to return the ID of the just updated 'row' you're talking about. You can use Eloquent to accomplish this.
You can use a route like this:
Route::put('demo/{id}', Controller#update);
Then in your update function in your controller you can get the ID.
Find the model with $model = Model::find(id);
do things with the data you get.
Like $model->name = Input::get('name');
Then use $model->save();
After saving you can do $model->id;
Now you get back the ID about the row you just updated.
Refer back to this question:
Laravel, get last insert id using Eloquent
But any way it'll always be at least 2 queries (a SELECT and an UPDATE in MySQL, however you do it)
You can check Laravel Eloquent if you want a "cleaner" way to to this.

Laravel nested relationship not working

I have a user model which stores basic user information such as username, password etc.
There are also 3 types of user, Student, Staff and Parent. Each type also has a seperate model. For example, there is a Student model which belongs to a User model.
I also have a relationships table, which stores relationships between students and parents. This relationship is stored in the User model.
If I do something like:
App\Student::first()->user->relations;
It happily returns a collection of related parents.
In my Students model, I have a method called hasParent() which accepts a given user ID, and checks to ensure the student has a parent with that id. In that method, I have the following:
public function hasParent($parent)
{
return $this->user->relations->where('id', $parent)->count() === 1;
}
However, this returns an error Cannot call 'where' on a non-object. If I debug further, $this->user->relations returns an empty array.
The problem is, like above, if I call the methods separately, I get the results I want.
So to clarify, if I run:
App\Student::first()->user->relations;
This returns a collection of users just fine.
In my Student model however, if I call:
$this->user
Then I get the correct student
If I call
$this->user->relations
I get an empty array. Which doesn't make sense! Can anyone shed any light on this, or what I might be doing wrong? If you need any further info, please let me know.
You need to call where on the relation like below.
public function hasParent($parent)
{
return $this->user->relations()->where('id', $parent)->count() === 1;
}
See the parenthesis after the relations. If you call the relation without the parenthesis Laravel returns you a collection. To get the builder you need to call the relation with the parenthesis.
I'd suggest - to avoid creating a huge query overhead (which you'll do by calling where and count on the Query builder, not the collection) - to do what you're doing already, except using Illuminate Collections filter-method:
public function hasParent($parent)
{
return $this->user->relations->filter(function($relation) use ($parent){return $entity->id === $parent;})->count() === 1;
}

Laravel mysql select result

I am trying to add a feature to a website built with Laravel.
There is a table containing vote numbers and user. I want to get the total points a user has in a certain category. I do not have any PHP or Laravel experience but said I would give this a shot.
$votes1 = UserVotes::select ('select vote from user_votes where feedback_id = ? and feedback_type = 1', Auth::user()->id);
This should return an object containing the vote amount. I want to interrogate the the object to check if the vote number is above a certain amount and then do something based on that being the case or not.
if vote > 50{
//do stuff
}
foreach ($votes1 as $vote1) {
echo $vote1->vote;
}
The query should return 1. I have verified this by querying the database, so the problem is with my understanding of Laravel or php. What I am doing wrong?
You don't need to construct your own SQL statement; Eloquent will do that for you.
If your models are set up in the default way, your query would look something like:
$votes = UserVotes::where('feedback_id', Auth::user()->id)
->where('feedback_type', 1)
->get();
You can then iterate over that as normal.
Additionally, if there is a relationship set up with the user model you could do something like
$votes = Auth::user()->votes()
->where('feedback_type', 1)
->get();
Check out the documentation here: http://laravel.com/docs/4.2/eloquent
assuming UsersVotes extends Model, here's how you should do it:
UsersVotes::select('vote')->where('feedback_type', 1)->where('feedback_id', Auth::user()->id)-get();

Categories