Laravel - How to prevent row getting deleted when there is dependent field - laravel

I am a laravel beginner. In my laravel CRUD project, I have these migration table
GroupTable
class Group extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('group', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('group_code');
$table->string('group_desc');
$table->timestamps();
});
}
CategoryTable
class Category extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('category', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('category_code');
$table->string('category_desc');
$table->timestamps();
});
}
ItemTable
class Item extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('item', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('item_code');
$table->string('item_desc');
$table->string('item_picture');
$table->string('item_cost');
$table->string('item_price');
$table->string('group_desc');
$table->string('category_desc');
$table->timestamps();
});
}
The group_desc and category_desc from ItemTable are foreign key from GroupTable and CategoryTable.
GroupController
public function destroy($id)
{
Group::find($id)->delete();
return response()->json(['success'=>'Group deleted successfully.']);
}
CategoryController
public function destroy($id)
{
Category::find($id)->delete();
return response()->json(['success'=>'Category deleted successfully.']);
}
This is the ajax delete function in my view of group
//Delete
$('body').on('click', '.deleteRecord', function () {
var id = $(this).data("id");
if(confirm("Are you sure want to delete? "))
{
$.ajax({
type: "DELETE",
url: "{{ url('group/delete') }}" + '/' + id,
success: function (data) {
table.draw();
},
error: function (data) {
console.log('Error:', data);
}
});
}
});
Before user want to delete the row on GroupTable or CategoryTable, how to display a message shows that the row are not allow to delete if ItemTable have data from GroupTable or CategoryTable?

If you are using the foreign key and if it is not a 'cascade' delete, then laravel won't allow you to delete the row when If the same Id used somewhere. for example,
$table->foreign('category_id')->references('id')->on('categories')->onDelete('cascade'); //inside item table. (CASCADE delete).
$table->foreign('category_id')->references('id')->on('categories');// non-cascade
assume, If you are using cascade delete, and you are trying to delete the category which is used in the item table, this will delete the category and related item lists in item table.
If you are using non-cascade method, It won't allow you to delete the category when it is used in item table.

If you follow Laravel's convention, things will be a lot easier to do. Might be longer at first but it gets better
(I am assuming you are using Laravel 8)
1: Table names are usually in plural forms, so your tables should be groups, categories and items
2: Next is to properly define foreign keys for groups and categories tables in items table like this
Schema::create('item', function (Blueprint $table) {
//...
$table->foreignId('group_id')->constrained();
$table->foreignId('category_id')->constrained();
//...
});
3: Define the respective relations in the 3 models
Group Model
namespace App\Models;
class Group extends Model
{
//...
public function items(){
return $this->hasMany(Item::class);
}
//...
}
Category model
namespace App\Models;
class Category extends Model
{
//...
public function items(){
return $this->hasMany(Item::class);
}
//...
}
Item Model
namespace App\Models;
class Item extends Model
{
//...
public function category(){
return $this->belongsTo(Category::class);
}
public function group(){
return $this->belongsTo(Group::class);
}
//...
}
All the above, while kind of long, will make your work easier later.
From here you can implement the following
GroupController
public function destroy($id)
{
$group = Group::withCount('items')
->find($id);
if($group->items_count < 1){
$group->delete()
return response()->json(['success'=>'Group deleted successfully.']);
}
return response()->json(['fail'=> 'Group has items, cannot be deleted.' ]);
}
CategoryController
public function destroy($id)
{
$category = Category::withCount('items')
->find($id);
if($category->items_count < 1){
$category->delete()
return response()->json(['success'=>'Category deleted successfully.']);
}
return response()->json(['fail'=> 'Category has items, cannot be deleted.' ]);
}

Related

When deleting data from the pivot table, also to delete from the main table

I want to ask a question. I am developing a project in Laravel 8. I have 3 tables products (id, name), urls (id, page_id, request_id) and product_url (product_id, url_id, site_id). When a product or url is deleted, I can delete it from the product_url table. There is no problem, but when I delete a data from the product table, I want to delete all the urls connected to that product from the product_url table, but it did not work, although I gave it onDelete ('cascade') in the product_url table. :( is there an easy way to achieve this?
Products migration:
Schema::create('products', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->timestamps();
});
Urls migration:
Schema::create('urls', function (Blueprint $table) {
$table->id();
$table->text('page_url');
$table->text('request_url')->nullable();
$table->timestamps();
});
Product_Url Pivot migration:
Schema::create('product_url', function (Blueprint $table) {
$table->foreignId('product_id')->constrained();
$table->foreignId('url_id')->constrained()->cascadeOnDelete();
$table->unsignedBigInteger('site_id');
});
Product Model:
use HasFactory;
protected $fillable = ['name'];
protected $hidden = ['created_at','updated_at'];
public function url()
{
return $this->belongsToMany(Url::class)
->withPivot('site_id');
}
public function sites()
{
return $this->belongsToMany(Site::class, 'product_url')
->withPivot('product_id','url_id');
}
Urls Model:
use HasFactory;
protected $fillable = ['page_url','request_url'];
protected $hidden = ['created_at','updated_at'];
public function products()
{
return $this->belongsToMany(Product::class)
->withPivot('site_id');
}
public function sites()
{
return $this->belongsToMany(Site::class, 'product_url')
->withPivot('product_id','url_id');
}
public function urls()
{
return $this->belongsToMany(self::class)
->withPivot('url_id');
}
Products Controller:
public function destroy($id)
{
$product = Product::find($id);
$product->url()->detach();
$product->delete();
return redirect()->route('product.index');
}
Urls Controller:
public function destroy($id)
{
$Url = Url::find($id);
$Url->products()->detach();
$Url->delete();
return redirect()->route('url.index');
}
To remove a many-to-many relationship record, use the detach method. The detach method will delete the appropriate record out of the intermediate table; however, both models will remain in the database:
//Detach a single url from product
$product->urls()->detach($urlId);
//Detach All urls from the product
$product->urls()->detach();
For convenience, detach also accept arrays of IDs as input:
$product->urls()->detach([1, 2, 3]);
And then you can delete your product model and related model.
/**
* Remove the specified resource from storage.
*
* #param \App\Models\Product $product
* #return \Illuminate\Http\Response
*/
public function destroy(Product $product)
{
$product->url()->detach();
$product->url()->delete();
$product->delete();
return redirect()
->route('product.index')
->withSuccess('Product başarılı şekilde silindi.');
}
solved the problem. I added the url to the ProductController destroy method with with, then I deleted the relation table first with $ product->url()->delete(), then I cleared the pivot table and deleted the product from the product table.
public function destroy($id)
{
$product = Product::with('url')->find($id) ?? abort(404,'Product Bulunamadı');
$product->url()->delete();
$product->url()->detach();
$product->delete();
return redirect()->route('product.index')->withSuccess('Product başarılı şekilde silindi.');
}

Eloquent - How to match data between tables of a single ID

Having multiple tables with data that relates to each other, i'm trying to display that data in a view using Laravel.
I must be confused with how Laravel runs its queries and I need help to sort how to do, what in PHP&SQL would be a left join.
My Asset Model:
public function category(){
return $this->hasOne(Category::class);
}
My Category Model:
public function asset()
{
return $this->belongsTo(Asset::class);
}
My Country Model:
public function country()
{
return $this->belongsTo(Asset::class);
}
And my AssetsController:
public function asset($id)
{
$asset = Asset::find($id);
return view('admin.assets.asset')->with('assets', $asset);
}
And the Router:
Route::get('/admin/assets/asset/{id}', [
'uses' => 'AssetsController#asset',
'as' => 'assets.asset'
//Show the Asset
]);
And the View:
<p><strong>Price:</strong>{{$assets->price}} €</p>
<p><strong>Description:</strong>{{$assets->description}}</p>
<p><strong>Country:</strong>{{$assets->country}}</p>
<p><strong>Category:</strong>{{$assets->name}}</p>
So in the 'asset.blade.php' I get the id from a previous index.blade.php that has a list of all the assets. I want to get via the ID, an asset page that displays the category name and the country name, instead of the ID that belongs to the Asset table.
So it should echo something like $assets->country->country_name and $assets->category->name
dd($asset);
EDIT: Additional information about migrations
categories_table migration
public function up()
{
Schema::create('categories', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('url');
$table->timestamps();
});
}
assets_table migration:
public function up()
{
Schema::create('assets', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->double('price',15,2);
$table->mediumText('description');
$table->integer('country');
$table->integer('category');
$table->integer('subcategory');
$table->integer('subsubcategory');
$table->integer('broker');
$table->string('featured');
$table->string('slug');
$table->timestamps();
});
}
Got it working.
class Category
{
public function asset()
{
return $this->hasMany(Asset::class);
}
}
class Asset
{
public function category()
{
return $this->belongsTo(Category::class);
}
}
use App\Asset;
use App\Category;
use App\Country;
use App\Subcategory;
use Illuminate\Http\Request;
class AssetController extends Controller
{
public function asset($id)
{
$asset = Asset::find($id);
return view('admin.assets.asset')
->with('asset', $asset)
->with('category', Category::all())
->with('subcategory', Subcategory::all())
->with('country', Country::all());
}
}
The other models have the same relationship towards the Asset model and vice-versa.
The View:
<p><strong>Category:</strong>{{$asset->category->name}}</p>
<p><strong>Sub-Category:</strong>{{$asset->subcategory->name}}</p>
It now shows the name matching the id of the corresponding tables.

on cascade delete on many to many relationship in laravel

I had used many to many relationship.i have three table named:product,tag,productstag. in here I want whenever I am going to delete a product it will also delete the relationship it had on productstag table.
public function up()
{
Schema::create('productstags', function (Blueprint $table) {
$table->integer('product_id');
$table->integer('tag_id');
$table->primary(['product_id','tag_id']);
$table->timestamps();
});
}
as you can see I have used product_id and tag_id as the primary key.
so I can't use this
$table->foreign('product_id')->references('id')->on('products')->onDelete('cascade');
$table->foreign('tag_id')->references('id')->on('products')->onDelete('cascade');
so what's the alternative?
on my product model
public function tags()
{
return $this->belongsToMany('App\Tag','productstags','product_id','tag_id')->withTimestamps();
}
on my tag model:
public function products()
{
return $this->belongsToMany('App\Product','Productstags','tag_id','product_id');
}
on my produduct destroy function:
public function destroy(Request $request,$id)
{
$product=Product::findOrFail($id);
if($request->file('image')==''){
$input=$request->except('photo_id');
}
else{
$input=$request->all();
unlink(public_path()."/images/".$product->image->name);
}
$product->delete($input);
Session::flash('deleted_user','the user has been deleted');
return redirect(route('product.index'));
}
It's not as pretty as cascade but you could override the delete method and do this:
public function delete(){
$this->name_of_the_relationship()->delete();
return parent::delete();
}

Why does Laravel Combine query is not working?

My Channels Model is :
class Channels extends Model
{
protected $fillable = ['title','slug'];
public function Discussion()
{
return $this->hasMany('App\Discussion');
}
}
Channel Model is:
class Channels extends Model
{
protected $fillable = ['title','slug'];
public function Discussion()
{
return $this->hasMany('App\Discussion');
}
}
Channel Migration File :
class CreateChannelsTable extends Migration
{
public function up()
{
Schema::create('channels', function (Blueprint $table) {
$table->increments('id');
$table->string('title');
$table->string('slug');
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('channels');
}
}
Discusion Migraion file
class CreateDiscussionsTable extends Migration
{
public function up()
{
Schema::create('discussions', function (Blueprint $table) {
$table->increments('id');
$table->integer('user_id')->unsigned();
$table->integer('channel_id')->unsigned();
$table->string('title');
$table->text('content');
$table->text('slug');
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('discussions');
}
}
**Controller is: **
class HomeController extends Controller
{
public function __construct()
{
$this->middleware('auth');
}
public function channel($slug)
{
$channel= Channels::where('slug',$slug)->first();
return view('channel')->with('discussion',$channel->Discussions);
}
}
And Finnaly the route is
Route::get('channel/{slug}',[
'uses' => 'HomeController#channel',
'as' => 'channel']);
**Now i am trying to fetch the data: **
$channel->Discussions
but it's giving me the error
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'discussions.channels_id' in 'where clause' (SQL: select * from discussions where discussions.channels_id = 1 and discussions.channels_id is not null)
I don't know what exactly to do now. Please Help me
First of all, it's very important to name your classes and its relationships appropriately.
Laravel makes some assumptions about your foreign keys based on your class names and relationship definitions. You could also specify the foreign keys if you want.
However, here's how i'd do it.
class Channel extends Model
{
protected $fillable = ['title','slug'];
public function discussions()
{
return $this->hasMany('App\Discussion');
}
}
Notice here the model name is called Channel. When we have this class name and relationship, Laravel will assume that the discussion model has a foreign key called channel_id which it does.
You also need to define an inverse relationship in your Discussion model
class Discussion extends Model
{
public function channel()
{
return $this->belongsTo('App\Channel');
}
}
Now, doing the following would work:
public function channel($slug)
{
$channel = Channel::whereSlug($slug)->first();
return view('channel')->with('discussion', $channel->discussions);
}
If you are tied and cannot change the model name for whatever reason, then you need to specify a foreign key on your relationship definitions.
For example:
public function discussions()
{
return $this->hasMany('App\Discussion', 'channel_id');
}
public function channel()
{
// This one can stay the same as Laravel will try to match `channel_id`
// on the discussion table to the id on the channels table
return $this->belongsTo('App\Channel');
}
Fore more information, read about Eloquent Relationships.
Sorry, I was already writing this up when #Julien Metral commented, but this is an extension of what he already said :)

(Laravel) Polymorphic relation through pivot table

Let's say, I have an Event model, which has more participants of various models (Player, Coach, Admin) through polymorphic relation and a pivot table (EventParticipant), which also contains a boolean column participate. I want to get participants through $event->participants which retrieves a collection of players, coaches, and admins through a polymorphic relation.
I have something similar created with standard non-polymorphic relation in trainings, like this:
class Training extends Model
{
/**
* Training has more players.
*/
public function players() {
return $this->belongsToMany('App\Player', 'training_player')
->using('App\TrainingPlayer')
->withPivot('participate');
}
}
class TrainingPlayer extends Pivot
{
/**
* The attributes that should be cast to native types.
*
* #var array
*/
protected $casts = [
'participate' => 'boolean'
];
}
How could this be modified in the case of events, where participants() can be either Player, Coach, or Admin model? (Maybe something with MorphPivot class but I can't imagine how.)
(Instead of player_id (in TrainingPlayer class) which refers to id of the Player model, there are two columns role and rollable_id (in EventParticipant class) which refers to id of the Player, Coach, or Admin model, respectively)
class Event extends Model
{
/**
* Event has more participants (players, coaches, or admins).
*/
public function participants() {
//
}
}
class EventParticipant extends MorphPivot
{
//
}
Any help would be appreciate. :) Thx
I have been looking for something similar and came up with a solution. As per Jonas comment, you can't have different Models in 1 related set but you can have 1 for each model using 1 pivot table.
You can now query this with \App\Team::find(1)->with(['managers', 'users'])->get();
Schema::create('associations', function (Blueprint $table) {
$table->id();
$table->string('association_type');
$table->integer('association_id');
$table->integer('team_id');
$table->integer('meta')->nullable();
$table->timestamps();
});
Schema::create('managers', function (Blueprint $table) {
$table->id();
$table->timestamps();
});
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->timestamps();
});
Schema::create('teams', function (Blueprint $table) {
$table->id();
$table->timestamps();
});
class Manager extends Model
{
public function teams()
{
return $this->belongsToMany('\App\Team', 'associations')->using('App\Association');
}
}
class Team extends Model
{
public function managers()
{
return $this->morphedByMany('App\Manager', 'association')->using('App\Association');
}
public function users()
{
return $this->morphedByMany('App\User', 'association')->using('App\Association');
}
}
class User extends Authenticatable
{
public function teams()
{
return $this->belongsToMany('\App\Team', 'associations')->using('App\Association');
}
}
// App/Association
use Illuminate\Database\Eloquent\Relations\MorphPivot;
class Association extends MorphPivot
{
protected $table = 'associations'; // not sure if this is needed
}

Resources