Can I create custom pivot table name using another M:M entity? - php

I'm still thinking is there a ways how can I create a custom pivot table name? Because I created a documents and users table has a many to many relationship with document_user which is my pivot table and this table was created for received document that user created. And I'm planning to create a another pivot table for document and user this table was for sent documents so I can have history. See my code below.
create_document_user_table
public function up()
{
Schema::create('document_user',function (Blueprint $table)
{
$table->increments('id');
$table->integer('user_id')->unsigned();
$table->integer('document_id')->unsigned();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->foreign('document_id')->references('id')->on('documents')->onDelete('cascade');
$table->unsignedInteger('sender_id')->nullable();
$table->foreign('sender_id')->references('id')->on('users')->onDelete('cascade');
$table->dateTime('dateReceived')->default(DB::raw('CURRENT_TIMESTAMP'));
$table->timestamp('dateModified')->default(DB::raw('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'));
});
}
documents_table
public function up()
{
Schema::create('documents', function (Blueprint $table) {
$table->increments('id');
$table->string('title');
$table->text('content');
$table->integer('category_id')->unsigned();
$table->foreign('category_id')->references('id')->on('categories')->onDelete('cascade');
$table->timestamps();
});
}
users_table
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->string('first_name');
$table->string('last_name');
$table->string('middle_name');
$table->string('email');
$table->string('username');
$table->string('address');
$table->string('password');
$table->string('remember_token');
$table->integer('role_permission_id')->unsigned();
$table->foreign('role_permission_id')->references('id')->on('roles_permissions_dt')->onDelete('cascade');
$table->timestamps();
});
}
This works well inserting records to my pivot table. What I'm planning to achieve is every-time I inserted a records for documents this will inserted too in my custom pivot table not only in my document_user pivot table. Any help would appreciated! Thanks for your info or tips.
UPDATE
#Mina thanks for the tips that you given but actually this is my insert or save for my pivot table. How can I inserted this in my revisions table?
DocumentController
public function postDocuments(Request $request)
{
$this->validate($request,
[
'title' => 'required|regex:/(^[A-Za-z0-9 ]+$)+/|max:255',
'content' => 'required',
'category_id' => 'required',
'recipient_id' => 'required',
]);
$document = new Document();
//Request in the form
$document->title = $request->title;
$document->content = $request->content;
$document->category_id = $request->category_id;
$document->save();
$user = Auth::user();
foreach($request->recipient_id as $recipientId)
{
$document->recipients()->sync([ $recipientId => ['sender_id' => $user->id]],false );
}
return redirect()->back();
}

You can call your pivot tables as you like.
As mentioned previously, to determine the table name of the
relationship's joining table, Eloquent will join the two related model
names in alphabetical order. However, you are free to override this
convention. You may do so by passing a second argument to the
belongsToMany method:
return $this->belongsToMany('App\Role', 'user_roles');
(Eloquent: Relationships #Many To Many)
In your case you would need to define the relations like this:
class User extends Model
{
// other stuff
createdDocuments()
{
return $this->belongsToMany('App\Document', 'document_user_created');
}
sentDocuments() // or receivedDocuments()
{
return $this->belongsToMany('App\Document', 'document_user_sent');
}
// other stuff
}
class Document extends Model
{
// other stuff
createdByUsers()
{
return $this->belongsToMany('App\User', 'document_user_created');
}
sentToUsers() // or sentFromUsers() or whatever it does mean
{
return $this->belongsToMany('App\User', 'document_user_sent');
}
// other stuff
}

You do not need another pivot table. You need a table like that:
public function up()
{
Schema::create('revisions', function (Blueprint $table) {
$table->increments('id');
$table->integer('document_id')->unsigned();
//Rest of table structure
$table->foreign('document_id')->references('id')->on('document_user')->onDelete('cascade');
$table->timestamps();
});
}
When you need to create a new Revision:
$document = new Document;
//add $document attributes
$user->documents()->save($document);
$document->recipients->each(function($recipient){
$id = $recipient->pivot->id;
Revision::create(['document_id'=>$id]);
})

Related

Laravel inserting data as many-to-many and getting error

In my application design i each category should be have one or multiple user which i should associate them together, this middle category should be have category_id referenced with categories table and user_id referenced with users table. now i have this migration files:
User:
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->nullable()->constrained();
$table->boolean('active')->default(0); //activating account in register
$table->string('name')->nullable();
$table->string('family')->nullable();
$table->string('username')->unique();
$table->string('email')->unique();
$table->timestamp('email_verified_at')->index()->nullable();
$table->string('password');
$table->rememberToken();
$table->softDeletes();
$table->timestamp('created_at')->useCurrent();
$table->timestamp('updated_at')->useCurrent();
});
categories:
Schema::create('categories', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('parent_id')->nullable();
$table->string('title');
$table->timestamp('created_at')->useCurrent();
$table->timestamp('updated_at')->useCurrent();
});
user_category:
Schema::create('user_category', function (Blueprint $table) {
$table->foreignId('category_id')->constrained()->onDelete('cascade');
$table->foreignId('user_id')->constrained()->onDelete('cascade');
$table->primary(
[
'category_id',
'user_id'
]
);
});
running migrate command work fine and i don't get any error, now i want to create a category and then created category should be referenced with user_category table with attach:
$category = \App\Models\Category::create(
[
'title' => 'php',
]
);
$category->owner()->attach(
[
'user_id' => 1
]
);
here i get this error:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'category_user_id' in 'field list'
(SQL: insert into `user_category`
(`category_id`, `category_user_id`) values (4, 1))
my Models:
category:
public function owner(): BelongsToMany
{
return $this->belongsToMany(CategoryUser::class );
}
user_category:
public function category(): BelongsToMany
{
return $this->belongsToMany(Category::class);
}
Laravel's naming convention for pivot tables is snake_cased model names in alphabetical order separated by an underscore.
So if you are to follow the conventions, the user_category table should be category_user instead.
Also, the relationships should be in plural form for many-to-many relations.
// Category model
public function users(): BelongsToMany
{
return $this->belongsToMany(User::class);
}
// User model
public function categories(): BelongsToMany
{
return $this->belongsToMany(Category::class);
}
If you are however deviating from the conventions, you can also set it up by customizing the relationships like so:
// Category model
public function owner(): BelongsToMany
{
return $this->belongsToMany(User::class, 'user_category', 'category_id', 'user_id');
}
// User model
public function category(): BelongsToMany
{
return $this->belongsToMany(Category::class, 'user_category', 'user_id', 'category_id');
}

Delete all posts related to a user in laravel

this is my posts table
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('user_id');
$table->integer('category_id')->unsigned()->index();
$table->integer('photo_id')->default(0)->unsigned()->index();
$table->string('title');
$table->text('body');
$table->timestamps();
$table->foreign('user_id')
->references('id')->on('users')
->onDelete('cascade');
});
}
this is my users table
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->integer('role_id')->index()->unsigned()->nullable();
$table->integer('photo_id')->index()->default(0);
$table->boolean('is_active')->default(0);
$table->string('name');
$table->string('email')->unique();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
}
these are the relations
public function posts() {
return $this->hasMany('App\Post');
}
public function user() {
return $this->belongsTo('App\User');
}
Delete code of the user
public function destroy($id)
{
$user = User::findOrFail($id);
if($user->photo_id !== 0) {
unlink(public_path() . $user->photo->path);
}
$user->delete();
Session::flash('deleted_user', 'The user has been deleted.');
return redirect('/admin/users');
}
Delete code of the post
public function destroy($id)
{
$post = Post::findOrFail($id);
if($post->photo_id !== 0) {
unlink(public_path() . $post->photo->path);
}
$post->delete();
return redirect('/admin/posts');
}
I am trying to delete all the posts related to a user when I delete a user.
For that, I am using foreign reference constraint in posts table as shown above
But it is not working when I delete the user. The posts are still there.
I dont know what I am doing wrong
This problem occurs most probably because the default table engine in your MySQL instance is set to MyISAM which doesn't support foreign keys. Trying to work with foreign keys on a MyISAM table would definitely not be a bug in Laravel. Although it would be nice if the Schema Builder could automatically set the engine to InnoDB if foreign keys are used.
so, use this line in your schema
$table->engine = 'InnoDB';
or alter the table with
ALTER TABLE table_name ENGINE=InnoDB;
May be help you.
Create you custom method like function destroyAllByUser()
and put the code like
DB::table('posts')->where('user_id', '=', 1)->delete();
I hope it may help
Delete user;
public function destroy($id)
{
$user = User::findOrFail($id);
if($user->photo_id !== 0) {
unlink(public_path() . $user->photo->path);
}
$user->posts->delete();
$user->delete();
Session::flash('deleted_user', 'The user has been deleted.');
return redirect('/admin/users');
}
A alternative way to solve that is to configure database.php file under laravel-project\config folder to work on InnoDB engine.
'mysql' => [
...
'engine' => 'InnoDB'
]
Now you don't need to worry when you using foreign keys...
REMEMBER - If you didn't configured this before you create your tables you should remigrate again.

How to create two Foreign Key on pivot table linked to one Primary Key

I'm trying to implement where I need to insert or save the current user that logged in. Inserting the recipient into user_id column works well but I need to manipulate who send the data I need to get the user id. I have two tables users and documents with a pivot table document_user.
users table
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->string('email');
$table->string('username');
$table->string('password');
$table->string('remember_token');
});
documents table
Schema::create('documents', function (Blueprint $table) {
$table->increments('id');
$table->string('title');
$table->text('content');
$table->integer('category_id')->unsigned();
$table->foreign('category_id')->references('id')->on('categories')->onDelete('cascade');
$table->timestamps();
});
document_user - pivot table
Schema::create('document_user',function (Blueprint $table)
{
$table->increments('id');
$table->integer('user_id')->unsigned();
$table->integer('document_id')->unsigned();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->foreign('document_id')->references('id')->on('documents')->onDelete('cascade');
$table->dateTime('dateReceived')->default(DB::raw('CURRENT_TIMESTAMP'));
});
DB Design:
Note! I only insert few column in my users table migration just to save a line of text.
Model
User
public function documents()
{
return $this->belongsToMany('App\Models\Document', 'document_user', 'user_id', 'document_id');
}
Document
public function recipients()
{
return $this->belongsToMany('App\Models\User', 'document_user', 'document_id', 'user_id');
}
Inserting records based on the user's choice to pivot table works well. But when I try to rollback my migration and alter my pivot table to this.
$table->integer('sender_id')->unsigned();
$table->foreign('sender_id')->references('id')->on('users')->onDelete('cascade');
I get a error it says:
SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (`webdev`.`document_user`, CONSTRAINT `document_user_sender_id_foreign` FOREIGN KEY (`sender_id`) REFERENCES `users` (`id`) ON DELETE CASCADE) (SQL: insert into `document_user` (`document_id`, `user_id`) values (34, 10))
How can I achieve inserting the current user in my pivot table? So I can track who sends and receive the data. Any help would appreciated! Cheers!
UPDATE 1:
Thanks to #Dastur for solving my issue.
document_user table
Schema::create('document_user',function (Blueprint $table)
{
$table->increments('id');
$table->integer('user_id')->unsigned();
$table->integer('document_id')->unsigned();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->foreign('document_id')->references('id')->on('documents')->onDelete('cascade');
$table->unsignedInteger('sender_id')->nullable();
$table->foreign('sender_id')->references('id')->on('users')->onDelete('cascade')->onUpdate('cascade');
$table->dateTime('dateReceived')->default(DB::raw('CURRENT_TIMESTAMP'));
});
I'm just having a hard time getting the id of the current user and insert this into sender_id column. Still don't have any idea to do this because I need to tracked the created documents of the users.
DocumentController
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Models\Document;
use App\Models\User;
use DB;
use Auth;
class DocumentController extends Controller
{
public function getDocuments()
{
//GETTING ALL THE ID OF THE USERS IN THE DATABASE EXCEPT THE ID OF CURRENT USER.
$resultRecipient = DB::table('users')->where('id', '!=', Auth::id())->get();
//GETTING ALL THE CATEGORIES.
$resultCategory = DB::table('categories')->get();
//VIEW
return view ('document.create')->with('resultRecipient', $resultRecipient)->with('resultCategory', $resultCategory);
}
public function postDocuments(Request $request)
{
$this->validate($request,
[
'title' => 'required|alpha_dash|max:255',
'content' => 'required',
'category_id' => 'required',
'recipient_id' => 'required',
]);
$document = new Document();
//Request in the form
$document->title = $request->title;
$document->content = $request->content;
$document->category_id = $request->category_id;
$document->save();
$document->recipients()->sync($request->recipient_id, false);
return redirect()->back();
}
}
UPDATE 2: According to #Dastur I need to create a another Model for my pivot table which is document_user.
UserDocument (Model)
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class UserDocument extends Model
{
protected $table = 'document_user';
}
I looked around a bit online, and found this post on laracasts: http://laravel.io/forum/09-18-2014-foreign-key-not-saving-in-migration. Also, this error is normally thrown when your trying to get a null value from another table and put it into a row that isn't nullable.
Edit:
What your doing here is very strange, I still think the pivot table isn't a smart option. Here's exactly what I would do:
Migrations:
First, I would create my users migration, simple enough:
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->string('email');
$table->string('username');
$table->string('password');
$table->rememberToken();
});
Next I would create my documents table:
Schema::create('documents', function(Blueprint $table) {
$table->increments('id');
$table->string('title');
$table->text('content');
$table->integer('category_id');
$table->integer('user_id');
$table->integer('sender_id');
$table->dateTime('dateRecieved')->default(DB::raw('CURRENT_TIMESTAMP'));
$table->timestamps();
});
Models:
In your user and category model you need the following method:
public function documents() {
$this->hasMany(/* Path to your document model */);
}
Finally, in your document model you need the following methods:
public function category() {
$this->belongsTo(/* Path to your category model */);
}
public function user() {
$this->belongsTo(/* Path to your user model */);
}
Document Controller
public function postDocuments(Request $request)
{
$this->validate($request,
[
'title' => 'required|alpha_dash|max:255',
'content' => 'required',
'category_id' => 'required',
'recipient_id' => 'required',
]);
/*
* This part not exactly sure what to do, because of I don't know what
* know what the difference is between the sender and the user, please
* elaborate.
*/
}

How to query pivot table using Eloquent in Laravel 5

I have a many-to-many relationship between my client and tag tables. A client can have many tags, and each tag can be related to multiple clients.
On the client show view, I'm trying to display the client info plus all tags associated to this client.
How do I change the query below to retrieve the client rows with all its related tags?
public function show($id)
{
$client = Client::findOrFail($id);
return view('clients.show')->with(['client' => $client]);
}
Client model
public function clienttag()
{
return $this->belongsToMany('App\Clienttag');
}
Clienttag model
public function client()
{
return $this->belongsToMany('App\Client');
}
Client_clientags table migration
public function up()
{
Schema::create('client_clienttag', function(Blueprint $table)
{
$table->integer('client_id')->unsigned();
$table->foreign('client_id')->references('id')->on('clients')->onDelete('cascade');
$table->integer('clienttag_id')->unsigned();
$table->foreign('clienttag_id')->references('id')->on('clienttags')->onDelete('cascade');
$table->timestamps();
});
}
Clients table migration
public function up()
{
Schema::create('clients', function(Blueprint $table)
{
$table->increments('id');
$table->string('first_name');
$table->string('last_name');
$table->rememberToken();
$table->timestamps();
});
}
Clienttags table migration
public function up()
{
Schema::create('clienttags', function(Blueprint $table)
{
$table->increments('id');
$table->string('tag');
$table->text('description');
$table->rememberToken();
$table->timestamps();
});
}
You can use "Eager Loading" methods like the following
public function show($id)
{
$client = Client::with('clienttag')->findOrFail($id);
return view('clients.show')->with(['client' => $client]);
}
Check documentation at http://laravel.com/docs/5.1/eloquent-relationships#eager-loading
Then at your view you can print your tags
#foreach ($client->clienttag as $tag)
{!! $tag->tagname!!} (or whatever your field in clienttags table name is)
#endforeach

Automaticlly attach to pivot table in Laravel 5

I currently have a Users to Groups Relationship (ManyToMany) with a pivot table group_user. I want the user to be able to create a group but once creating the group, how do I make it, that the creator becomes member of this group?
Currently I have
My Pivot Table (group_user):
Schema::create('group_user', function(Blueprint $table)
{
$table->integer('group_id')->unsigned()->index();
$table->foreign('group_id')->references('id')->on('groups')->onDelete('cascade');
$table->integer('user_id')->unsigned()->index();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->timestamps();
});
My Groups table (groups):
Schema::create('groups', function(Blueprint $table)
{
$table->increments('id');
$table->string('name');
$table->timestamps();
});
My Users table (users):
Schema::create('users', function(Blueprint $table)
{
$table->increments('id');
$table->string('username')->unique();
$table->string('email')->unique();
$table->string('name');
$table->string('lastname');
$table->string('password', 60);
$table->rememberToken();
$table->timestamps();
});
My models ofcourse have the following: User.php
public function groups()
{
return $this->belongsToMany('App\Group');
}
Group.php
public function users()
{
return $this->belongsToMany('App\User');
}
What create function should I write in my controller so that when a User creates a Group, that he automaticly becomes member of this group (automaticlly make the pivot relationship)?
This should work, make sure you implement validation, ect.
public function store(Request $request)
{
$group = Group::create([ // <-- if names are unique. if not, then create is fine
'name' => $request->get('name')
]);
auth()->user()->groups()->attach([$group->id]);
return view('your.view');
}
Also make sure to add:
use App\Group;
See attach() and detach().
$user = User::find(1);
$user->groups()->attach(10); // pivot relationship of this user to group of id 1.
OR
$group = Group::find(10);
$user->groups()->save($group);
For many groups of this user:
$user->groups()->sync(array(1, 2, 3));

Categories