Laravel: Delete relationships - php

I'm using Laravel 4.2. I have 2 models. I want to make a list of items with a button to delete the relationship.
My models:
Word {
public function posts()
{
return $this->belongsToMany('Post');
}
}
Post {
public function words() {
return $this->hasMany('Word');
}
}
My desired list for Word with id=1
post1 x
post2 x
postn x
When I click to any x button I want to delete the relationship (not the word nor post!).
My approach (blade):
#foreach ($word->posts as $post)
{{{ helper_delete_button_with($post->id) }}}
#endforeach
In conclusion, which is the best way to delete the relation?
Making a function to WordController with a route like
/word/{$wordid}/{$postid} (so I have to have a hidden input with
$wordid)?
Making a controller like PageWordController with the destroy function
(so I have to know the id of relationship, and I don't know how to
get it using relationships)?
Any other solution?
Thank you.

well, i think that you have 3 tables, one for posts, one for words and one for the relationship, in this case is better the second option because you only have to have the id of the relationship and destroy directly.

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 way of defining a one-to-one relationship using intermediate table to avoid foreign key cycle

Laravel rookie here. I've got titles and reviews. A title can have many reviews, a review belongs to a title.
So far, my tables look unsurprisingly as follows:
titles:
- id (pk)
...
reviews:
- id (pk)
- title_id (fk)
...
Now, I want a title to have zero or one (so-called) top reviews.
My first instinct was to add a nullable top_review_id column to the titles table, but I wanted to avoid a cycle of foreign keys. So instead, I created a third table called top_reviews as follows:
top_reviews:
- id
- title_id (fk, unique)
- review_id (fk, unique)
That way a title is guaranteed to have at most one top review and a review cannot be the top review for multiple titles. (I do realise that it is still possible to have a top review entry where the review actually belongs to a different title, but that's okay.)
My question is how do I wire that up cleanly in Laravel (7.x) ideally using Eloquent ORM relationships and following the framework's best practices?
So far I've got this:
class Title extends Model {
public function reviews() { return $this->hasMany(Review::class); }
public function topReview() { /* ??? */ }
}
class Review extends Model {
public function title() { return $this->belongsTo(Title::class); }
}
I've considered the following:
I could manually build something ugly like return Review::find(DB::table('top_reviews')->select('review_id')->where('title_id', $this->id)->get());, but I suspect there is a nicer Laravelesque way for these trivial relationships.
Simply using hasOne() doesn't seem to be the solution either since it will assume a different table name (namely reviews instead of top_reviews) and there is no way to specify a custom table.
Defining a model TopReview seems clumsy, but perhaps it is my best bet. I suppose that would allow me to define topReview() as hasOneThrough(Review, TopReview).
Feel free to correct me if I'm on the wrong track.
Thanks.
With belongsToMany relationship
class Title extends Model
{
public function reviews()
{
return $this->hasMany(Review::class);
}
public function topReview()
{
return $this->belongsToMany(Review::class, 'top_reviews', 'title_id', 'review_id');
}
}
Anyways you can skip that top_reviews table and just save top_review_id into your titles table and I think that's more efficient
Depend on you requirement
You one only one top reviews so I think you should Has One Through but if you want to have multi top reviewer on 1 title you should use Many To Many.
Has One Through
I will remove title_id if i use it
top_reviews:
- id
- review_id (fk, unique)
class Title extends Model {
public function topReview() {
return $this->hasOneThrough(TopReview::Class, Review::class);
}
}
Many To Many
top_reviews:
- id
- title_id (fk, unique)
- review_id (fk, unique)
class Title extends Model
{
public function topReview()
{
return $this->belongsToMany(Review::class, 'top_reviews', 'title_id', 'review_id');
}
}
but as you describe above. I think I will use has one through.

Access relation of pivot table in Laravel

I have three models Bill, Product and Process. Bill has a ManyToMany relationship with Product and the pivot table have some extra fields. I write the Bill model class like follow:
<?php
class Bill extends Model
{
function products(){
return $this->belongsToMany(\App\Product::class)
->withPivot('process_id') // the id of the Process
->withTimestamps();
}
}
The Process model is a simple table with an id and a name. I am associating the id in the pivot table for reference the Process, the name could change over time but still referring the same concept so I can't associate the name.
The show page for a single Bill lists the products associated in a table like follow:
#foreach($bill->products as $product)
<tr>
<td>{{$product->barcode}}</td>
<td>{{$product->pivot->process_id}}</td>
</tr>
#endforeach
So the problem is that I need the name of the process, but I have the id. I'm not sure how I could get the name.
Thanks
I think you can use an own Pivot Model, e.g. ProductBill in order to achieve this.
class ProductBill extends Pivot {
public function process() {
return $this->belongsTo(Process::class);
}
}
By using this model in your products relation on Bill
class Bill extends Model {
function products() {
return $this->belongsToMany(\App\Product::class)
->withPivot('process_id')
->using(ProductBill::class)
->withTimestamps();
}
}
When accessing $product->pivot you should get an instance of ProductBill now, hence you should be able to do the following:
<td>{{$product->pivot->process->name}}</td>
(Unfortunatelly I not able to doublecheck right now :/)
Without having a direct relation to Process you will likely need to add a helper on your Product model to get the name of Process.
In your Product model:
public function processName($processId) {
return Process::where('id', $processId)->pluck('name')->first();
}
In your view:
<td>{{$product->processName($product->pivot->process_id) }}</td>
There may be a better way, but concept this should work.
I know it's not the most elegant of solutions. but you could always simply do:
Process::find($product->pivot->process_id)->name;
I wouldn't advice this though as you are looping through an array already, so the overheads to doing something like this would be quite large.
Another solution would be to create a Pivot class called say BillProductPivot which would have a relationship to return both Product and Process, then when you call them you should be using eager loading to get the relationships. end product might look like this:
$bill->load('productPivots', 'productPivot.process', 'productPivot.product');
#foreach($bill->productPivots as $productPivot)
<tr>
<td>{{$productPivot->product->barcode}}</td>
<td>{{$productPivot->process->name}}</td>
</tr>
#endforeach

Laravel: get a model through another table

I have gotten a model called User. in this model i have a function called UserActivity where i return this:
return $this->hasMany('App\UserActivity', 'userid');
After this i have a function that gets the activity name from the Activity table, but i do not know how to do this, i have currently gotten this:
public function Activity() {
return $this->hasManyThrough('App\UserActivity', 'App\Activity', 'userid', 'activityid');
}
And in my view i want to use this like this:
$activiteiten = \App\User::find(Auth::user()->id);
dd($activiteiten->UserActivity()->Activity());
But then i get an error saying this:
Call to undefined method Illuminate\Database\Query\Builder::Activity() (View: /var/www/vhosts/cpned.nl/intranet.cpned.nl/laravel/resources/views/dashboard.blade.php)
I can do it using inner joins but i am really wondering if i can do this with Laravel models. I do not know how currently, and because i don't know what the name is for the function in laravel i can't find it either, so I am sorry if this will be a duplicate.
My tables have the following keys and foreign keys:
Users: pk: id
user_activities: pk: userid, activityid
activities: pk: id
Thank you in advance!
You can try using eager loading.
So something like:
$activiteiten = \App\User::with('UserActivity.Activity')->find(Auth::user()->id);
dd($activiteiten->UserActivity->Activity);
Edit: So then you can do something like this:
foreach($activiteiten->UserActivity as $user_activity) {
foreach($user_activity->Activity as $activity) {
print_r($activity);
}
}
Shows how you can loop through the relationships.
The Activity function should be in the User model. So then you can call it this way:
$activiteiten = \App\User::find(Auth::user()->id);
dd($activiteiten->Activity); //gives you a collection of activities
However, you need belongsToMany function rather than hasManyThrough since the relationship between users and activities is many-to-many.
This mean you cut down going through the Intermediate model. You can check the doc on belongsToMany to understand what to do better.
A side note: Why are your functions name starting with capital letter?
Also using hasMany relations should affect the function name as well, so that 'activity' becomes 'activities' (just to keep the functionality and interpretation in sync).
Update:
//User model
public function activities() {
return $this->belongsToMany('App\Activity', 'user_activity', 'userid', 'activityid');
}
This uses the 'user_activity' table, to find the relationship between users and activities, so that you may now access by User::first()->activities for example.

Laravel 4 - Pivot Tables

I understand how relationships in Laravel (Eloquent) work, but let's say I've got a many to many relationship between two models, call them Posts and Tags.
So our relationship is fine, from Posts I can access all the related Tags, and from Tags I can get all related Posts.
But... I have the ID of a row in the pivot table, and want to return both the Post and Tag relating to this row, how would I go about doing this?
EDIT:
Let's introduce a third Model, called Foo. This contains the ID of the row in the pivot table,
is there anyway I can create a relationship from this to the two other models?
A Model for your pivot:
<?php
class PostTagPivot
{
public function Post()
{
return $this->belongsTo('Post');
}
public function Tag()
{
return $this->belongsTo('Tag');
}
}
And retrieve Post and Tag via:
$Post = PostTagPivot::find($fooBarId)->Post;
$Tag = PostTagPivot::find($fooBarId)->Tag;

Categories