Deleting a table row in relations with another tables - php

I have a Post table with the following fields (id, name, state_id, city_id, category_id, subcategory_id)
and PostPhoto table with the following fields (id, photo_url, post_id)
The Post Model has
public function user(){
return $this->belongsTo(User::class);
}
public function category(){
return $this->belongsTo(Category::class);
}
public function subCategory(){
return $this->hasOne(SubCategory::class);
}
public function photos(){
return $this->hasMany(PostPhoto::class);
}
public function city(){
return $this->belongsTo(City::class);
}
and PostPhoto Model has
public function post()
{
return $this->belongsTo(Post::class);
}
Now if I want to delete a particular post, it'll fail due to foreign key constrains. How do I delete the Post together with postphotos associated with it completely from database. And each post can have multiple photos also. Please any suggestion will be appreciated. Thanks

Either you can make post_id as a foreign key and make cascade relation on update and delete which will automatically delete post photos on post deletion.
otherwise, you can try below code.
try {
\DB::beginTrasaction();
PostPhotos::where('post_id', 'your-post-id')->delete();
Post::where('id', 'your-post-id')->delete();
\DB::commit();
} catch(\Exception $exception) {
\DB::rollback();
}

In your Post model add booted method .boot method will call whenever model executes.if you are calling post delete then static deleting closure will exicutes
if you are using latest larvel 8
protected static function booted()
{
static::deleting(function($post) {
$post->photos()->delete();
});
}
if you are using older laravel version then
protected static function boot()
{
parent::boot();
static::deleting(function($post) {
$post->photos()->delete();
});
}
Update
$post=Post::find($id);
$post->photos()->delete();
$post->delete();

Related

Select specific column in laravel eloquent's with method

I am using laravel 5.6 and i want to get user posts with comments (only id field)
User Model
public function posts()
{
return $this->hasMany('App\Post');
}
Post Model
public function user()
{
return $this->belongsTo('App\User');
}
public function comments()
{
return $this->hasMany('App\Comment');
}
Comment Model
public function post()
{
return $this->belongsTo('App\Post');
}
In my controller i am using this code to get user posts with their comments
$posts = $request->user()->posts()->with(['comments' => function($query) {
$query->select(['id']);
}]);
But its not working...
When i comment $query->select(['id']); it works fine but returns Comment model all fields. I want to only select id field.
What i am missing here?
You also have to select the foreign key column (required for matching the results):
$posts = $request->user()->posts()->with('comments:id,post_id');
If you want to only one column, you can use ->pluck('id')
https://laravel.com/docs/5.6/collections#method-pluck

Laravel user can follow Comment, Category, Post etc

I wanted to implement a follow system where a User can follow Comment, Category, Post and more. I have tried using Laravel Polymorphic relations for this but could not wrap my head around it. If someone can guide me it will be great.
Here is what I have tried.
User Model
public function categories()
{
return $this->morphedByMany(Category::class, 'followable', 'follows')->withTimestamps();
}
Category Model
public function followers()
{
return $this->morphMany(Follow::class, 'followable');
}
Follow Model
public function followable()
{
return $this->morphTo();
}
public function user()
{
return $this->belongsTo(User::class);
}
Follow migration
Schema::create('follows', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('user_id');
$table->morphs('followable');
$table->timestamps();
});
How can I get the all the categories, comments followed by a user. Also how I can get the Followers of a Cateogry or Commnets etc.
Please help.
You dont't need Follow model.
All you need is pivot table like so
followable
user_id - integer
followable_id - integer
followable_type - string
add folowers method to all your classes which you need to follow
For example
Category Model
public function followers()
{
return $this->morphToMany(User::class, 'followable');
}
Then in User Model
public function followers()
{
return $this->morphToMany(User::class, 'followable');
}
public function followedCategories()
{
return $this->morphedByMany(Category::class, 'followable')->withTimestamps();
}
public function followedComments()
{
return $this->morphedByMany(Comment::class, 'followable')->withTimestamps();
}
public function followedPosts()
{
return $this->morphedByMany(Post::class, 'followable')->withTimestamps();
}
// and etc
public function followedStuff()
{
return $this->followedCategories
->merge($this->followedComments)
->merge($this->followedPosts);
}
Then you can reach your goal by accessing to followers of certain category, comment or post or whatever you wish(if it followable of courcse)
For example:
$folowers = $category->folowers;
// will return all followers this category
$all = $user->followedStuff();
// will return collection of all things followable by the user

How to get forienkeys data in laravel models

I am implementing chat in my application using laravel. I have these Models:
`Users: id, name `
`Pages: id, user_id(foreign_key from Users table (id)), name`
`Posts: id, page_id(foreign_key from Pages table (id)), user_id(foreign_key from Users table (id)), body`
`Comments: id, post_id(foreign_key from Posts table (id)), user_id(foreign_key from Users table (id)), body`
I need to query followings
Get all posts of user with comments and with user
Get all posts of a page with comments and with user
I am using below query to get all posts of a page, but how can I include its comments and user details;
Page::with('post')->findOrFail($pageId)->post;
EDIT
USER Model: User.php
public function page() {
return $this->hasMany('Page');
}
public function post() {
return $this->hasMany('Post');
}
Page Model: Page.php
public function user()
{
return $this->belongsTo('User');
}
public function post() {
return $this->hasMany('Post');
}
Post Model: Post.php
public function user()
{
return $this->belongsTo('User');
}
public function page() {
return $this->belongsTo('Page');
}
public function comments() {
return $this->hasMany('Comment');
}
Comment Model: Comment.php
public function user()
{
return $this->belongsTo('User');
}
public function post() {
return $this->belongsTo('Post');
}
As your relationships are not that clear to me, i can only provide some hints where to look.
Getting posts of a user with comments sounds like laravel query scopes: http://laravel.com/docs/5.1/eloquent#query-scopes
You may also query for relationship existence: http://laravel.com/docs/5.1/eloquent-relationships#querying-relations
And Lazy Eager Loading could also help to retrieve the necessary instances.

`Trying to get property of non-object` using `hasManyThrough` relationship laravel 5

I am trying to develop a blog using Laravel 5 in which i have to show comment along with user on post.
Here is my database table schema.
User
id
name
Posts
id
post_content
user_id
Comments
id
comment
user_id
post_id
Here is my User Model
public function posts()
{
return $this->hasMany('App\Models\Posts');
}
public function comments(){
return $this->hasManyThrough('App\Models\Comments','App\Models\Posts');
}
Here is my Posts Model
public function user()
{
return $this->belongsTo('App\Models\User');
}
public function comments(){
return $this->hasMany('Ap\Models\Comments');
}
Here is my Comment Model
public function posts()
{
return $this->belongsTo('App\Models\Posts');
}
public function user(){
return $this->posts->name;
}
Here is my code how i am accessing user name
$comments = Comments::find(1);
$comment['comment'] = $comments->comment;
$comment['user_name'] = $comments->name;
$comment['post_id'] = $comments->posts->id;
may be i am not getting in right direction? if i am doing right then why it is not working.
In laravel 5 you do not call the model as you are doing. since the models are stored in the app folder just call like. Plus I think you need to define the relationship
class User extends Model {
public function phone()
{
return $this->hasOne('App\Phone');
}
}
class Phone extends Model {
public function user()
{
return $this->belongsTo('App\User');
}
}
$phone = Phone::find(1);
For the case of foreign keys and more regarding the Eloquent relationships in laravel 5 just follow the documentation on the laravel website. Make sure to look at dynamic properties of that are allowed by eloquent
http://laravel.com/docs/5.0/eloquent

Automatically deleting related rows in Laravel (Eloquent ORM)

When I delete a row using this syntax:
$user->delete();
Is there a way to attach a callback of sorts, so that it would e.g. do this automatically:
$this->photo()->delete();
Preferably inside the model-class.
I believe this is a perfect use-case for Eloquent events (http://laravel.com/docs/eloquent#model-events). You can use the "deleting" event to do the cleanup:
class User extends Eloquent
{
public function photos()
{
return $this->has_many('Photo');
}
// this is a recommended way to declare event handlers
public static function boot() {
parent::boot();
static::deleting(function($user) { // before delete() method call this
$user->photos()->delete();
// do the rest of the cleanup...
});
}
}
You should probably also put the whole thing inside a transaction, to ensure the referential integrity..
You can actually set this up in your migrations:
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
Source: http://laravel.com/docs/5.1/migrations#foreign-key-constraints
You may also specify the desired action for the "on delete" and "on
update" properties of the constraint:
$table->foreign('user_id')
->references('id')->on('users')
->onDelete('cascade');
Note: This answer was written for Laravel 3. Thus might or might not works well in more recent version of Laravel.
You can delete all related photos before actually deleting the user.
<?php
class User extends Eloquent
{
public function photos()
{
return $this->has_many('Photo');
}
public function delete()
{
// delete all related photos
$this->photos()->delete();
// as suggested by Dirk in comment,
// it's an uglier alternative, but faster
// Photo::where("user_id", $this->id)->delete()
// delete the user
return parent::delete();
}
}
Hope it helps.
Relation in User model:
public function photos()
{
return $this->hasMany('Photo');
}
Delete record and related:
$user = User::find($id);
// delete related
$user->photos()->delete();
$user->delete();
There are 3 approaches to solving this:
1. Using Eloquent Events On Model Boot (ref: https://laravel.com/docs/5.7/eloquent#events)
class User extends Eloquent
{
public static function boot() {
parent::boot();
static::deleting(function($user) {
$user->photos()->delete();
});
}
}
2. Using Eloquent Event Observers (ref: https://laravel.com/docs/5.7/eloquent#observers)
In your AppServiceProvider, register the observer like so:
public function boot()
{
User::observe(UserObserver::class);
}
Next, add an Observer class like so:
class UserObserver
{
public function deleting(User $user)
{
$user->photos()->delete();
}
}
3. Using Foreign Key Constraints (ref: https://laravel.com/docs/5.7/migrations#foreign-key-constraints)
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
As of Laravel 5.2, the documentation states that these kinds of event handlers should be registered in the AppServiceProvider:
<?php
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
User::deleting(function ($user) {
$user->photos()->delete();
});
}
I even suppose to move them to separate classes instead of closures for better application structure.
It is better if you override the delete method for this. That way, you can incorporate DB transactions within the delete method itself. If you use the event way, you will have to cover your call of delete method with a DB transaction every time you call it.
In your User model.
public function delete()
{
\DB::beginTransaction();
$this
->photo()
->delete()
;
$result = parent::delete();
\DB::commit();
return $result;
}
To elaborate on the selected answer, if your relationships also have child relationships that must be deleted, you have to retrieve all child relationship records first, then call the delete() method so their delete events are fired properly as well.
You can do this easily with higher order messages.
class User extends Eloquent
{
/**
* The "booting" method of the model.
*
* #return void
*/
public static function boot() {
parent::boot();
static::deleting(function($user) {
$user->photos()->get()->each->delete();
});
}
}
You can also improve performance by querying only the relationships ID column:
class User extends Eloquent
{
/**
* The "booting" method of the model.
*
* #return void
*/
public static function boot() {
parent::boot();
static::deleting(function($user) {
$user->photos()->get(['id'])->each->delete();
});
}
}
Using Constrained()
After Laravel 7, new foreignId() and constrained() methods are available for defining relationship constraint in database. OnDelete() method can be used on these methods to automatically delete related records.
Old style
$table->unsignedBigInterer('user_id');
$table->foreign('user_id')
->references('id')
->on('users')
->onDelete('cascade');
New style
$table->foreignId('user_id')
->constrained()
->onDelete('cascade');
I would iterate through the collection detaching everything before deleting the object itself.
here's an example:
try {
$user = User::findOrFail($id);
if ($user->has('photos')) {
foreach ($user->photos as $photo) {
$user->photos()->detach($photo);
}
}
$user->delete();
return 'User deleted';
} catch (Exception $e) {
dd($e);
}
I know it is not automatic but it is very simple.
Another simple approach would be to provide the model with a method. Like this:
public function detach(){
try {
if ($this->has('photos')) {
foreach ($this->photos as $photo) {
$this->photos()->detach($photo);
}
}
} catch (Exception $e) {
dd($e);
}
}
Then you can simply call this where you need:
$user->detach();
$user->delete();
Add delete function on model that you want to delete
Define relations of models
for example in this instance:
/**
* #return bool|null
*/
public function delete(): ?bool
{
$this->profile()->delete();
$this->userInterests()->delete();
$this->userActivities()->delete();
$this->lastLocation()->delete();
return parent::delete();
}
And relations in user model are:
public function profile()
{
return $this->hasOne(Profile::class, 'user_id', 'id');
}
public function userInterests()
{
return $this->hasMany(userInterest::class, 'user_id', 'id');
}
public function userActivities()
{
return $this->hasMany(userActivity::class, 'user_id', 'id');
}
public function lastLocation()
{
return $this->hasOne(LastLocation::class, 'user_id', 'id');
}
This way worked for me on Laravel 8:
public static function boot() {
parent::boot();
static::deleted(function($item){
$item->deleted_by = \Auth::id(); // to know who delete item, you can delete this row
$item->save(); // to know who delete item, you can delete this row
foreach ($item->photos as $photo){
$photo->delete();
}
});
}
public function photos()
{
return $this->hasMany('App\Models\Photos');
}
Note: deleting in this syntax $user->photos()->delete(); not worked for me...
In my case it was pretty simple because my database tables are InnoDB with foreign keys with Cascade on Delete.
So in this case if your photos table contains a foreign key reference for the user than all you have to do is to delete the hotel and the cleanup will be done by the Data Base, the data base will delete all the photos records from the data base.
Here are the perfect solutions.
# model
public function order_item_properties()
{
return $this->hasMany(OrderItemProperty::class, 'order_id', 'id');
}
public function order_variations()
{
return $this->hasMany(OrderItemVariation::class, 'order_id', 'id');
}
# controller
$order_item = OrderItem::find($request->order_id);
$order_item->order_item_properties()->delete();
$order_item->order_variations()->delete();
$order_item->delete();
return response()->json([
'message' => 'Deleted',
]);
Or you can do this if you wanted, just another option:
try {
DB::connection()->pdo->beginTransaction();
$photos = Photo::where('user_id', '=', $user_id)->delete(); // Delete all photos for user
$user = Geofence::where('id', '=', $user_id)->delete(); // Delete users
DB::connection()->pdo->commit();
}catch(\Laravel\Database\Exception $e) {
DB::connection()->pdo->rollBack();
Log::exception($e);
}
Note if you are not using the default laravel db connection then you need to do the following:
DB::connection('connection_name')->pdo->beginTransaction();
DB::connection('connection_name')->pdo->commit();
DB::connection('connection_name')->pdo->rollBack();
It’s better to use onDelete cascade when defining your model’s migration. This takes care of deleting the model’s relations for you:
e.g.
$table->foreign(’user_id’)
->references(’id’)->on(’users’)
->onDelete(’cascade’);
If you happen to find yourself thinking about how to delete a model and its relations to a level greater than 3 or 4 nested relations, then you should consider redefining your model's relationships.
$table->foreignId('user_id')->constrained('user')->cascadeOnDelete();
or
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
yeah, but as #supersan stated upper in a comment, if you delete() on a QueryBuilder, the model event will not be fired, because we are not loading the model itself, then calling delete() on that model.
The events are fired only if we use the delete function on a Model Instance.
So, this beeing said:
if user->hasMany(post)
and if post->hasMany(tags)
in order to delete the post tags when deleting the user, we would have to iterate over $user->posts and calling $post->delete()
foreach($user->posts as $post) { $post->delete(); } -> this will fire the deleting event on Post
VS
$user->posts()->delete() -> this will not fire the deleting event on post because we do not actually load the Post Model (we only run a SQL like: DELETE * from posts where user_id = $user->id and thus, the Post model is not even loaded)
You can use this method as an alternative.
What will happen is that we take all the tables associated with the users table and delete the related data using looping
$tables = DB::select("
SELECT
TABLE_NAME,
COLUMN_NAME,
CONSTRAINT_NAME,
REFERENCED_TABLE_NAME,
REFERENCED_COLUMN_NAME
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
WHERE REFERENCED_TABLE_NAME = 'users'
");
foreach($tables as $table){
$table_name = $table->TABLE_NAME;
$column_name = $table->COLUMN_NAME;
DB::delete("delete from $table_name where $column_name = ?", [$id]);
}

Categories