Laravel only delete a model if no related model exist - php

Lets say i have a model called 'manufacturer' and this model has one to many relation with another model 'vehicle'. Now i dont want to let users delete a manufacturer if there are any vehicles associated with this model.
//In Manufacturer model
public function vehicles()
{
return $this->hasMany('Vehicle');
}
And in the repository/controller i have another method to check for it.
public function checkAssociatedVehicles($id)
{
return Manufacturer::with('vehicles')->find($id)->toJson();
}
This does outputs the manufacturer data with all the associated vehicles. but this is not efficient, so i just want to check that if there is even one vehicle then dont delete the manufacturer.

I believe you'd want to use the has method to make sure the manufacture has some vehicles.
$manufacture = Manufacturer::has('vehicles')->find($id);
Then you'd just want to make sure !is_null($manufacture)

As you have a vehicles() method in your Manufacturer model, you may just do this:
Use the whereDoesntHave method to filter all Manufacturers without vehicles before deleting them:
Manufacturer::whereDoesntHave('vehicles')->get();
From the API:
Add a relationship count condition to the query with where clauses.

You can return the count of total vehicles like this:
return [
'count' => Manufacturer::with('vehicles')->find($id)->count()
];
Now you can just check the count.

This will return boolean on whether there is an vehicles on the manufacturer.
return (Manufacturer::with([
'vehicles'=> function($query){
return $query->limit(1);
}])->find($id)->vehicles);
You can add foreign key and prima key on the vehicles to reduce the number of column being query for performance concern.
return $query->limit(1)->select('id','manufacturer_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.

Eloquent Relationship without Pivot Model

I have 3 tables:
customer: fields: id, name
item: fields: id, name
customer_items: fields: customer_id, item_id, qty
Customer and Item have their separate Models as we would expect.
Question: How would I relate these two(Customer and Item) without having a pivot model.
I want to directly get customer items using $customer->items instead of doing $customer->customerItem->items which I find unnecessary since I don't want to track customerItems & customer by item.
Also, I cannot directly use customer_items table for Item model as I might need to retrieve all items in its controller.
As pointed out by #kerbholz (but they didn't create an answer so here it is), in your customer model you need the following function:
public function items()
{
return $this->belongsToMany('App\Item');
}
Which assumes your Item model class sits within App. You may also do the reverse in your Item model.
Now you should be able to do $customer->items and get a collection of items.
If you want to include the qty field then you need:
public function items()
{
return $this->belongsToMany('App\Item')->withPivot('qty');
}
Note that you still need the pivot table, you can't escape that, but you can now navigate it in a more elegant manner.
creat customer_items table and include custmer_item_id and user_id . in User model include this function
public function basket() // example
{
return $this->belongsToMany(User::class, 'customer_items', 'user_id', 'custmer_item_id');
}

Laravel Eloquent ORM: get value from passing through multiple one to many relationships

I have three tables which are users, loans, statuses
The relationship is like this:
A user can have many loans. a loan has many status steps. in the statuses table I have a column called status, basically it telsl this step yes, no. pending sort of situation.
the table structure look like this:
users table
->id
->...
loans table
->id
->...
->user_id (it is the foreign key ->references('id')->on('users');
statuses table
->id
->...
->status (can be "yes", "no", "pending")
->...
->loan_id (it is the foreign key ->references('id')->on('loans');
the models look like this:
in the User model :
public function loans(){
return $this->belongsToMany('App\Loan');
}
in the Loan model:
public function users(){
return $this->belongsToMany('App\User');
}
public function statuses(){
return $this->hasMany('App\Status');
}
in the Status model:
public function loan(){
return $this->belongsTo('App\Loan');
}
My question is how to get the status yes number for each user. say I have five users, each user have multiple loans. each loan have, say 20 steps. but different loan many have different yes steps . I would like to use laravel eloquent ORM to get a array tell me each user get how many yes at certain time. So I would be able to loop through this array in my front end blade file to display users progress. thanks!
Laravel's Collection, which you get when you use Eloquent, is great for this kind of operation. Say you want to get the 'yes' statuses of one user:
//yesStatuses is itself a Collection, but it can be used in a foreach like an array
$yesStatuses = $user->loans
->map(function ($loan) {
return $loan->statuses;
})
->filter(function ($status) {
return $status->status === 'yes';
});
//Number of statuses === 'yes'
$yesStatuses->count();
//If you need it as a regular array
$yesStatuses->toArray();
When you query your users table you should take care of loading your loans and statuses eagerly, otherwise you'll be performing many queries for this operation. Something like this:
$users = App\User::with('loans.statuses')->get();
More on this:
Eloquent Collections
Eager loading of Eloquent Models
Thanks for your help, SrThompson!
I think map() function in the collection really helps. since my goal is to loop through the users to show each user's attributes plus count the "yes" status for that user. So I added my 'statuscount' into the user collection as:
$user['statuscount']=$yesStatusesCount;
my working code block like this:
$users=User::with('loans')->with('loans.statuses')
->map(function ($user){
$yesStatuses = $user->loans->map(function ($loan) {
return $loan->statuses->where('status','yes')->count();
});
$yesStatusesCount=array_sum($yesStatuses->toArray());
$user['statuscount']=$yesStatusesCount;
return $user;
});
Then, in my balde file, I would be able to display the number of "yes" status of the user in the #foreach loop. Thanks again!

ID lost despite left join

I am using a left join to combine two tables based on a common column but I seem to lose the primary key id in my left table. Every other column is returned
My controller:
$prod_manu = $request->prod_manu;
return $data = Product::leftJoin('manufacturers','products.Manufacturer_id','=','manufacturers.id')
->where('products.Manufacturer_id',$prod_manu)
->get();
Product Model:
class Product extends Model{
public function types(){
return $this->belongsTo('App\types','Type_id');
}
public function manufacturers(){
return $this->belongsTo('App\manufacturers','Manufacturer_id');
}
}
This is probably because both Product and manufacturers have an id column, so one of them gets overwritten.
Instead of using the left join, you can use eloquent's with method.
If you have your relationships and class structure setup properly you should be able to access the Manufacturer's products like this:
$manufacturer = Manufacturer::where('id', $prod_manu)->with('products')->get();
return $manufacturer->products;
Also note that class names should always be singular and start with a capital letter in laravel. App\manufacturers Shouldn't be valid. Please read the article I linked above about setting up your eloquent relationships.

How to get the count of Belongsto in Laravel?

I want to get the count of the elements of materials count.
SubCategory:
public function materials(){
return $this->hasMany('App\Material');
}
Materials:
public function subcategories(){
$this->belongsTo('App\SubCategory','SubCategoria_id');
}
I want to get the count of the elements that have the subcategory_id
'quantidade' => $subcategory->material->count()
I'm getting error in 1054 Unknown column 'material.subcategory_id'
You should use the relationship to count. This will count the items using the DB. You should use the function like this:
'quantidade' => $subcategory->materials()->count()
And in the model, you have to inform the id in hasMany too:
public function materials(){
return $this->hasMany('App\Material' ,'SubCategoria_id');
}
You should also notice that it would be better if you name yours classes and variables according to Laravel patterns.
Does your material table have a subcategory_id column? This error could indicate it doesn't exist, therefore it's failing when you try the relationship.
As long as the column exists, the relationship should be valid and you should be able to count the results.
Since you're using a custom foreign key, you need to specify it. So, change the relation to:
public function materials()
{
return $this->hasMany('App\Material', 'SubCategoria_id');
}
And then use the proper relationship name:
$subcategory->materials()->count()

Categories