Laravel - Set data to null before deleting column from other table - php

I have 2 tables. (1) being users, and (2) being foods.
In the [users] table, there is a food_id bracket that is linked as a foreign key to an item/column's id in the other table [foods].
I am able to make a reservation of a [food] column, although I want to be able to press a 'confirm' button on the item once reserved, which will delete the item's targetted column in the Database.
Although since both tables are linked with a foreign key, I know that I need to set the parent key to null in order to be able to fully delete the target column. Otherwise it throws me an error that I can't delete/update a parent item with children objects.
(my food's object primary ID being linked to the authenticated user's food_id foreign key.)
This current code I tried only throws me the following error: "Call to a member function onDelete() on null"
$foodsId = User::find(auth()->user()->foods_id);
$foodsId->onDelete('set null');
$foodsId->save();
$foodDel = Foods::find($id);
$foodDel->delete();
Don't exactly know what to think here.

You could edit the foreign key constraint to do this automatically, but what you're trying to do can be done with these lines.
$food = Food::findOrFail(auth()->user()->food_id);
auth()->user()->fill(['food_id' => null])->save(); // use forceFill() if food_id is not in the User's fillable fields.
$food->delete();
To have this happen automatically, you could make a migration with the command
php artisan make:migration changeFoodIdForeignKeyConstraint --table=users
function up()
{
Schema::table('users', function (Blueprint $table) {
$table->dropForeign(['food_id']);
$table->foreign('food_id')
->references('id')->on('foods')
->onUpdate('cascade')
->onDelete('set null');
});
}
Another option is to use a model event on the Food model
class Food extends Model
{
/**
* The "booted" method of the model.
*
* #return void
*/
protected static function booted()
{
static::deleting(function ($food) {
User::where('food_id', $food->id)->update(['food_id' => null]);
});
}

Actually have found my own solution. I went to directly target my first foreign key, to then asign a null parameter to it, THEN fetch the targetted ID of my item and delete its column.
$user = auth()->user();
$user->food_id = null;
$foodDel = Foods::find($id);
$foodDel->delete();

Related

Laravel Many to Many Relationship mixes up id's in insert

I got the the problem, that the id's of the FK's for my pivot table get mixed up before insert and thus I get an error because a category with an ID that high isn't available.
Here's how I try to insert:
$newAdvertisement = new Advertisement();
$newAdvertisement->name = $request->name;
$newAdvertisement->save();
$newAdvertisement->advertisementCategories()->attach($request->selectedCategory);
The Advertisement migration and class function:
public function advertisementCategories(): BelongsToMany
{
return $this->belongsToMany(
AdvertisementCategory::class,
'advertisement2advCategory',
'advertisementCategory_id',
'advertisement_id'
);
}
//Advertisement
Schema::create('advertisement', function (Blueprint $table) {
$table->id();
$table->string('name')->default('');
});
And here the category class function and migration:
public function advertisements(): BelongsToMany
{
return $this->belongsToMany(
Advertisement::class,
'advertisement2advCategory',
'advertisement_id',
'advertisementCategory_id'
);
}
Schema::create('advertisement2advCategory', function (Blueprint $table) {
$table->foreignId('advertisement_id');
$table->foreign('advertisement_id')
->references('id')
->on('advertisement')
->onDelete('cascade');
$table->foreignId('advertisementCategory_id');
$table->foreign('advertisementCategory_id')
->references('id')
->on('advertisementCategory')
->onDelete('cascade');
});
In the error message you can clearly see, that the advertisement_id and advertisementCategory_id get mixed up...
In this example an advertisement with the ID 19 has been created and the selected category (that exists) has ID 4, but the generated sql is trying to insert an advertisement with id 4 and a cagetory with id 19. Just How?
I'm using the latest version of laravel and php 8.1.1
SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (umarketdb.advertisement2advCategory, CONSTRAINT advertisement2advcategory_advertisementcategory_id_foreign FOREIGN KEY (advertisementCategory_id) REFERENCES advertisementCategory (i) (SQL: insert into advertisement2advCategory (advertisementCategory_id, advertisement_id`) values (19, 4))
You need to switch the order of the 3rd and 4th arguments to belongsToMany. It is a little confusing with how things are named but as per the documentation:
"The third argument is the foreign key name of the model on which you are defining the relationship, while the fourth argument is the foreign key name of the model that you are joining to." - Laravel 8.x Docs - Eloquent - Relationships - Many to Many - Model Structure
So, if you are defining a relationship on the Category model to Advertisement lets say then the 3rd argument is the foreign key that references categories (category_id). The 4th argument is the foreign key that references the other model/table advertisements (advertisement_id).

Many-to-many in same model

I need to use a many-to-many relationship to one model. I have an Article model, and I want to make a function so that other articles, typically recommended, can be attached to one article.
This is the function, it should work correctly.
$article = Article::where('id', $request->article_id)->first();
$articles_ids = json_decode($request->articles_ids);
$article->articles()->attach($articles_ids);
I have a question about how to create a table of relations in the database and in the model correctly, I did it like this, but even the migration does not work for me
Model
public function articles()
{
return $this->belongsToMany('App\Models\Article');
}
public function article_recommended()
{
return $this->belongsToMany('App\Models\Article');
}
db
Schema::create('article_article_recommended', function (Blueprint $table) {
$table->unsignedBigInteger('article_recommended_id')->nullable();
$table->foreign('article_recommended_id')
->references('id')->on('article_recommended')->onDelete('set null');
$table->unsignedBigInteger('article_id')->nullable();
$table->foreign('article_id')
->references('id')->on('articles')->onDelete('cascade');
});
error in migration
SQLSTATE[HY000]: General error: 1824 Failed to open the referenced table
'article_recommended' (SQL: alter table `article_article_recommended` add constraint
`article_article_recommended_article_recommended_id_foreign` foreign key
(`article_recommended_id`) references `article_recommended` (`id`) on delete set null)
What are you trying to do should be fairly simple , it's like a following system where each user is related to many users ( same model ) but also same table !
you are here trying to create another table which i consider unnecessary , you only need two tables
articles & recommendations
and the recommendation tabel will act as a pivot table for articles with its self thus creating a many to many relationship with the same table .
Article.php
public function recommendations() {
return $this->belongsToMany(Article::class , 'recommendations' , 'article_id' , 'recommended_article_id');
}
create_recommendations_table.php
Schema::create('recommendations', function (Blueprint $table) {
$table->primary(['article_id','recommended_article_id']);
$table->foreignId('article_id');
$table->foreignId('recommended_article_id');
$table->timestamps();
$table->foreign('article_id')->references('id')->on('article')->onDelete('cascade');
$table->foreign('recommended_article_id')->references('id')->on('articles')->onDelete('cascade');
});
usage
$article->recommendations()->attach($articles_ids);
be sure that the ref-column has exact the same type as the source column (signed, unsigned etc.)

Laravel updateExistingPivot with multiple primary keys

Problem
I want to update a row in a pivot table that have 2 primary keys. But updateExistingPivot want only a single primary key.
$user = App\User::find(1);
$user->roles()->updateExistingPivot($roleId, $attributes);
My DB-tables
Campaign
User
Campaign_user (primary keys are user_id and campaign_id)
My Question
Should I change my pivot table so it only have 1 primary key called id. Or can I keep it with 2 primary keys, and still update it, with Eloquent?
I think for best practice you should add a key id in your Campaign_user table, structure should:
Campaign_user
id|user_id|campaign_id
In User Model
public function campaign()
{
return $this->belongsToMany('Campaign', 'Campaign_user','user_id','campaign_id')->withPivot('extra attribute if any');
}
In Campaign Model
public function users()
{
return $this->belongsToMany('User', 'Campaign_user','campaign_id','user_id')->withPivot('extra attribute if any');
}
Now your code is:
$user = App\User::find($userId);
$user->campaign()->updateExistingPivot($campaignId, array('any attribute'=>$value));

Primary key and foreign key - Table entry deletion

Table: Users (For storing user login and personal info)
Primary key- ID
"id" is using as foreign key in the tables complaints and books.
My question is... How can I delete user entries in complaints and
books table when I want to delete a user from users table (in laravel
5.2)
Thanks in advance
In your model you can leverage model events to achieve what you want:
public static function boot() {
parent::boot();
static::deleting(function($user) {
if(!$user->books->isEmpty()) {
foreach($user->books as $book) {
$book->delete();
}
}
if(!$user->complaints->isEmpty()) {
foreach($user->complaints as $complaint) {
$complaint->delete();
}
}
});
}
https://laravel.com/docs/5.2/eloquent#events
You can just add an ->onDelete('cascade') to your foreign key (in your migration) if you generally want to delete related rows.
For further information:
https://laravel.com/docs/5.2/migrations#foreign-key-constraints

Laravel: How should I update this relationship?

Suppose I have the following tables:
User:
-userID
-userName
...
Exercises:
-exerciseID
...
User model:
<?php
use Illuminate\Auth\UserInterface;
use Illuminate\Auth\Reminders\RemindableInterface;
class User extends Eloquent implements UserInterface, RemindableInterface {
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'users';
protected $primaryKey = 'userID';
...
public function hasPassedExercises() {
return $this->hasMany('Exercise', 'exerciseID');
}
}
I want to say that a User has many completedExercises, so when the user completes an exercise, I update the model like so:
Route::post('dbm/userPassedExercise', function () {
$user = User::with('hasPassedExercises')->find($_POST['userID']);
$exercise = Exercise::find($_POST['exerciseID']);
$user->hasPassedExercises->save($exercise);
});
However, this has no effect on any underlying table, as far as I have understood. I'm trying to make sense of the documentation and see how it applies to my problem. So my question is what is the right course of action to do here.
Should I create a table users_completed_exercises that has userID and exerciseID as foreign keys, and if so, how do I link them to my user when I do the update? Or is there a more elegant solution?
Indeed, you have to use a relationship table (called pivot table).
In the laravel documentation, you have to name your pivot table with your tables name ordered by their name (you have not to, but it's prefered).
We'll take your naming convention so : users_completed_exercises
So here we shoud have this :
users:
- userId // Unsigned Int PRIMARY KEY AUTO_INCREMENT
Exercises:
- exerciseId // Unsigned Int PRIMARY KEY AUTO_INCREMENT
users_completed_exercises:
- id // Unsigned Int PRIMARY KEY AUTO_INCREMENT
- exerciseId // Unsigned Int FOREIGN KEY REFERECES EXERCICES ON ID
- userId // Unsigned Int FOREIGN KEY REFERECES USERS ON ID
On the user model, you should have :
public function passedExercises()
{
// Alphabetical order of your id's are here, very important because laravel
// retreives the good ID with your table name.
return $this->belongsToMany('Exercise', 'users_completed_exercises', 'exerciseId', 'userId');
}
And the inverse on Excercise Model
public function usersWhoPassed()
{
// Alphabetical order of your id's are here, very important because laravel
// retreives the good ID with your table name.
return $this->belongsToMany('User', 'users_completed_exercises', 'exerciseId', 'userId');
}
Retreiving infos are now, so easy.
Route::post('dbm/userPassedExercise', function () {
// Don't use $_POST with laravel, they are exceptions indeed, but avoid as much as
// possible.
$user = User::find(Input::get('userId'));
$exercise = Exercise::find(Input::get('exerciseId'));
// Very important, use () on relationships only if you want to continue the query
// Without () you will get an Exercises Collection. Use ->get() or ->first() to end
// the query and get the result(s)
$exercise->usersWhoPassed()->save($user);
});
You can easly check if user has passed an exercise too
Route::get('/exercises/{id}/passed_users', function($id)
{
$exercise = Exercise::find($id);
if ($exercise->usersWhoPassed()
->where('userId', '=', Input::get('userId'))->count()) {
return 'User has passed';
}
return 'User has failed';
});

Categories