inserting data into database laravel 5.6 relation belongstomany pivot table - php

i wonder to know how to insert data into pivot table when using a belongsto many relationship i can read the data from database now but i dont know how to store data in database and in my invoice_product table here is the code
model of invoce :
class Invoice extends Model
{
protected $fillable = ['title','description','client_id','product_id'];
public function user() {
return $this->hasOne('App\Client','id','client_id');
}
public function products()
{
return $this->belongsToMany('App\Product', 'invoice_product', 'invoice_id')
->withPivot('product_quantity')
->as('invoice_products_pivot');
}
}
controller of invoice :
public function store(Request $request)
{
//Validate
$request->validate([
'title' => 'required|min:3',
'description' => 'required',
]);
$invoices = Invoice::create([
'title' => $request->title,
'description' => $request->description,
'client_id' => $request->client_id,
'product_id' => $request->product_id,
]);
return redirect('admin/invoices/' . $invoices->id);
}
this stores an invoice into invoice table but i want to get the client_id and product_id or ids cause it must be multiple products and save them into invoice_product table which migration is down here
public function up()
{
Schema::create('invoice_product', function (Blueprint $table) {
$table->increments('id');
$table->integer('client_id');
$table->integer('product_id');
$table->integer('product_quantity');
$table->timestamps();
});
}

to insert data into intermediate or pivot table for many to many relationshiop
you can use attach eloquent method like below
$invoice->products()->attach($product_id)
$product->invoices()->attach($invoice_id)
but your invoice product migration looks a bit odd it should be like this
public function up()
{
Schema::create('invoice_product', function (Blueprint $table) {
$table->increments('id');
$table->integer('invoice_id'); // id of the invoice table
$table->integer('product_id'); // id of the product table
$table->integer('product_quantity'); // client_id should go to the invoice table
$table->timestamps();
});
}

Related

Add multiple categories for articles

I need to add several categories for a new article. I will write down everything I do in order:
migration of categories
public function up()
{
Schema::create('blog_categories', function (Blueprint $table) {
$table->BigIncrements('id');
$table->string('title', 128);
$table->timestamps();
});
}
migration of articles
public function up()
{
Schema::create('articles', function (Blueprint $table) {
$table->BigIncrements('id');
$table->string('title', 128);
$table->string('slug', 64);
$table->string('subtitle', 256)->nullable();
$table->timestamps();
});
}
creating another migration article_blog_category_table
public function up()
{
Schema::create('article_blog_category', function (Blueprint $table) {
$table->unsignedBigInteger('blog_category_id')->nullable();
$table->foreign('blog_category_id')
->references('id')->on('blog_categories')->onDelete('set null');
$table->unsignedBigInteger('article_id')->nullable();
$table->foreign('article_id')
->references('id')->on('articles')->onDelete('cascade');
});
}
Doing belongsToMany in models
article model
public function blog_categories()
{
return $this->belongsToMany('App\Models\BlogCategory');
}
category model
public function articles()
{
return $this->belongsToMany('App\Models\Article');
}
}
Next, I write the function for adding an article in the controller (I think there is no need to write the function for adding a category, everything is clear there)
Articles controller
public static function saveArticle(Request $request) {
$validator = Validator::make($request->all(), [
'blog_category_id' => 'required|numeric',
'title' => 'required|max:128',
'slug' => 'required|unique:articles|max:64',
'subtitle' => 'max:256',
]);
if ($validator->fails()) {
return response()->json([
'message' => $validator->errors()->first()
], 422);
}
$article = new Article();
$blog_category = BlogCategory::where('id', $request->blog_category_id)->first();
if(!$blog_category){
return response()->json([
'message' => 'Blog category not found'
], 404);
}
$article->blog_category_id = $request->blog_category_id;
$article->title = $request->title;
$article->slug = $request->slug;
$article->subtitle = $request->subtitle;
$article->save();
return Article::where('slug', $article->slug)->first();
}
I have a method in the function to add one category. The question of how to add here so that you can add several categories, I cannot figure it out. You need something like $article->blog_categories()->attach($request->blog_category_id); but how to apply it correctly?
Your naming convention is complicating your task.
Rename table in categories migration:
Schema::create('categories', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->timestamps();
});
Also for simplicity, rename the joint (pivot) table
Schema::create('article_category', function (Blueprint $table) {
// You don't need a table id here
$table->foreignId('category_id')->index();
$table->foreignId('article_id')->index();
$table->unique(['article_id', 'category_id']);
// You also don't need timestamps
});
Defining relationships in the models:
// Article model
public function categories()
{
return $this->belongsToMany(\App\Models\Category::class);
}
// Category model
public function articles()
{
return $this->belongsToMany(\App\Models\Article::class);
}
Article controller
public function store() // No need to make the function static
{
$data = validator(request()->all(), [
// To save many categories, send them as an array
'categories' => 'array',
'categories.*' => [\Illuminate\Validation\Rule::exists('categories', 'id')], // Checks if category id is in the db
'title' => 'required|max:128',
'slug' => 'required|unique:articles|max:64',
'subtitle' => 'string',
])->validate();
$article = Article::create(
\Illuminate\Support\Arr::except($data, 'categories');
);
// Save categories
$article->categories()->attach($data['categories']);
return $article;
}
According to Documentation
Many To Many Relationships
Attaching / Detaching
//You can pass an array of id's categories in attach method:
$article->blog_categories()->attach($categories_ids_array);
/*
*if you want to pass more columns value you can pass an associative array of
*column names with their values e.g attach($categories ids array, an array of
*more columns with their values)
*/
$article->blog_categories()->attach($categories_ids_array, ['column_name' =>
$column_values]);

Seeding Faker Factory Model for many-to-many relations

I know it's some dump mistake, but i can't locate it for a while. I have table Meals and table Ingredient with relation many-to-many. Want to input some fake data and create ingredient_meal table. I post some code below but when i go for db:seed --class=MealSeeder i get this error
SQLSTATE[HY000]: General error: 1364 Field 'title' doesn't have a default value (SQL: insert into meals
(updated_at, created_at) values (2020-05-29 16:26:55, 2020-05-29 16:26:55))
guessing that this factory isn't working right but can't find solution anywhere, i could pass this issue by putting nullable() in migration and updateing my data but that don't seems me right. Thanks in advance for help!
MealFactory.php
$factory->define(App\Meal::class, function (Faker $faker) {
return [
'title' => $faker->realText($maxNbChars = 10),
'description' => $faker->realText($maxNbChars = 20),
];
});
$factory->define(App\Ingredient::class, function (Faker $faker) {
static $id = 1;
return [
'title' => $faker->realText($faker->numberBetween(10,15)),
'slug' => $id++, //$faker->unique()->numberBetween(1,20),
];
});
Model Meal.php
protected $fillable = ['title','description'];
public function ingredients()
{
return $this->belongsToMany('App\Ingredient');
}
Model Ingredient.php
protected $fillable = ['title'];
public function meals()
{
return $this->belongsToMany('App\Meal');
}
Meal migration
Schema::create('meals', function (Blueprint $table) {
$table->increments('id');
$table->string('title');
$table->text('description');
$table->string('status')->default('created');
$table->string('category')->nullable();
$table->timestamps();
//$table->foreign('category_id')->references('id')->on('categories')->onDelete('cascade');
});
MealSeeder.php
factory(App\Meal::class, 10)->create()->each(function ($meals) {
$meals->ingredients()->save(factory(App\Ingredient::class)->make());
});
factory(App\Ingredient::class, 10)->create()->each(function ($ingredients) {
$ingredients->meals()->save(factory(App\Meal::class)->make());
});
// Get all the ingredients attaching up to 3 random ingredient to each meal
$ingredients = App\Ingredient::all();
// Populate the pivot table
App\Meal::all()->each(function ($meal) use ($ingredients) {
$meal->ingredients()->attach(
$ingredients->random(rand(1, 3))->pluck('id')->toArray()
);
});
EDIT:
I moved on, didn't found right solution. I set in migrations, nullable() propethy for 'title' and 'description' attributes. Run seeder and than fill out all null data trought DB::update. It look so ugly now :(

Laravel Why pivot table is empty?

I have a simple relationship in laravel eroquent
Here is the bidders table creation
public function up()
{
Schema::create('bidders', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('params_name');
$table->string('params_value');
$table->string('bidder_name');
$table->timestamps();
});
}
Here is bidder_parameter table creation
public function up()
{
Schema::create('bidder_parameters', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('bidder_id');
$table->foreign('bidder_id')->references('id')->on('bidders')->onDelete('cascade');
$table->timestamps();
});
}
Here is a bidder model
class Bidder extends Model
{
protected $table = 'bidders';
protected $fillable = [
"params_name",
"params_value",
"bidder_name"
];
public function page()
{
return $this->hasMany('App\Page');
}
public function parameters()
{
return $this->hasMany('App\BidderParameter');
}
}
and here is BidderParameter model
class BidderParameter extends Model
{
public function parameters()
{
return $this->belongsTo('App\Bidder');
}
}
Here is parameter controller for inserting data to database
public function store(Request $request){
// dd($request);
if($request->ajax())
{
$rules = array(
'params_name.*' => 'required',
'params_value.*' => 'required',
'bidders_name.*' => 'required'
);
$error = Validator::make($request->all(), $rules);
if($error->fails())
{
return response()->json([
'error' => $error->errors()->all()
]);
}
$params_name = $request->params_name;
$params_value =$request->params_value;
$bidders_name =$request->bidders_name;
for($count = 0; $count < count($params_name); $count++)
{
$data = array(
'params_name' => $params_name[$count],
'params_value' => $params_value[$count],
'bidders_name' => $bidders_name[$count],
);
$insert_data[] = $data;
// dd($insert_data);
}
Bidders:insert($insert_data);
return response()->json([
'success' => 'Data Added successfully.'
]);
}
}
Now when I submit data to the database 'bidders tablehave data saved into it butbidder_parameter` is empty
What is wrong with my code?
Many to many relationship contains 3 tables. 2 tables are main and 3rd table is combination of those 2 tables' primary keys.
For example
User can have many roles
and
Role can belong to many users
so that requires many to many relationship
So we need a database schema like this:
Users table -> (id, name ,...) e.g. Jonn Doe with id 1
Roles table -> (id, name, ...) e.g. SUPER_ADMIN with 1
role_user table (id, user_id, role_id) role_id is foreign key of roles table and user_id is foreign key of users table
Now in Model classes:
In User Model
public function roles(){
return $this->belongsToMany(Role::class,'role_user','user_id','role_id');
}
Now in Roles class
public function users(){
return $this->belongsToMany(User::class,'role_user','role_id','user_id');
//note keys are in opposite order as in roles() method
}
Now you can call function from User instance and Role instance where you want
$user->roles // as collection
$user->roles() // as eloquent instance
Also
$role->users // as collection
$role->users() // as eloquent instance
You can read more here

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

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]);
})

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.
*/
}

Categories