Add multiple categories for articles - php

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

Related

laravel migration changes not reflecting

i changed my post_id foreign key to posts_id in images migration in order for the relationship to work. however after changing, the insertion of images in images table is still picking post_id which is giving me an error column not found. i tried cache:clear and config but nothing worked. post_id is nowhere in my code
postcontroller
public function store( Request $request )
{
$data = request()->validate([
'user_id' => 'required',
'about' => 'required',
'category' => '',
'expire_date' => '',
]);
if (Auth::guard('web')->check())
{
$user = Auth::user();
$post = new Post();
/*$post = $user->posts()->create([
'about' => $data['about'],
'category' => $data['category'],
'expire_date' => $data['expire_date'],
]);*/
if($request->hasFile('image'))
{
$files = $request->file('image');
foreach($files as $file)
{
$name = time().'-'.$file->getClientOriginalName();
$name = str_replace('','-',$name);
echo $name;
$file->move('images',$name);
$post->images()->create(['image' => $name ]);
}
}
$user = Auth::guard('web')->id() ;
return redirect()->route('home',['user'=>$user]);
}
}
postmodel
public function posts()
{
return $this->belongsTo(User::class);
}
images model
class images extends Model
{
protected $fillable = [
'posts_id',
'image'
];
public function posts(){
return $this->belongTo(Posts::class);
}
}
posts migration
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->uuid('user_id')->nullable;
$table->uuid('admin_id')->nullable;
$table->string('category')->nullable;
$table->string('about');
$table->timestamps();
});
}
images migration
public function up()
{
Schema::create('images', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('posts_id');
$table->string('image');
$table->timestamps();
$table->index('posts_id');
});
}
Try to change Images model
public function posts(){
return $this->belongTo(Posts::class);
}
to
public function posts(){
return $this->belongTo(Posts::class, 'posts_id');
}
Why would you change it? Laravel can handle this for you if leave it to post_id. Otherwise you’ll have to update the relation posts in your images model by adding extra arguments. See the documentation: laravel.com/docs/7.x/eloquent-relationships
public function posts(){
return $this->belongTo(Posts::class, 'posts_id');
}

Booking system with many to many laravel relationship

I'm trying to make an app where airbnb hosts can have a log of their bookings, I created three models: Home, Guest and Booking. Booking being the main player, I also think there should be a pivot table but I'm not sure which models should it link... I decided to go with booking_guest but I'm getting the following error when I create a booking:
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'booking_id' cannot be null (SQL: insert into `booking_guest` (`booking_id`, `guest_id`) values (?, 1), (?, 2))
I do something like this in my BookingController:
public function create(Request $request)
{
$guestIds = Guest::latest()->take(2)->pluck('id');
$home = Home::findOrFail(1);
$booking = new Booking();
$booking->home_id = $home->id;
$booking->guests()->attach($guestIds);
$booking->save();
return response()->json([
'booking' => $booking,
]);
}
I'm not feeling too sure about this configuration, could you guys share some light on me.
These are my models:
class Home extends Model
{
public function guests()
{
return $this->belongsToMany('App\Models\Guest', 'guest_home', 'home_id', 'guest_id');
}
public function bookings()
{
return $this->hasMany('App\Models\Booking');
}
}
class Booking extends Model
{
public function guests()
{
return $this->belongsToMany('App\Models\Guest', 'booking_guest', 'booking_id', 'guest_id');
}
}
class Guest extends Model
{
public function bookings()
{
return $this->belongsToMany('App\Models\Booking', 'booking_guest', 'guest_id', 'booking_id');
}
}
My migrations:
//Booking guest pivot table
Schema::create('booking_guest', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('booking_id')->index();
$table->foreign('booking_id')->references('id')->on('bookings')->onDelete('cascade')->onUpdate('cascade');
$table->unsignedInteger('guest_id')->nullable()->index();
$table->foreign('guest_id')->references('id')->on('guests')->onDelete('cascade')->onUpdate('cascade');
$table->timestamps();
});
Schema::create('guests', function (Blueprint $table) {
$table->increments('id');
$table->string('fullName');
$table->text('country');
$table->timestamps();
});
Schema::create('bookings', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('home_id')->index();
$table->foreign('home_id')->references('id')->on('homes')->onDelete('cascade')->onUpdate('cascade');
$table->timestamp('entryDate')->nullable();
$table->timestamp('exitDate')->nullable();
$table->timestamps();
});
Schema::create('homes', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('host_id')->index();
$table->foreign('host_id')->references('id')->on('hosts')->onDelete('cascade')->onUpdate('cascade');
$table->string('fullAddress')->unique();
$table->integer('rooms')->unique();
$table->timestamps();
});
As you can see from here:
public function create(Request $request)
{
...
$booking = new Booking(); // <-- here
$booking->guests()->attach($guestIds); // <-- here
$booking->save(); // <-- here
...
}
you are creating a new instance of Booking, then associating to it a Guest and then saving the instance of Booking.
However ->attach(...) tries to associate the Booking with the Guest, but the Booking does not exists at that time on the DB.
I would suggest to use Booking::create, so that after that statement, the booking exists on the DB and so you can attach to it the Guest:
public function create(Request $request)
{
$guestIds = Guest::latest()->take(2)->pluck('id');
$home = Home::findOrFail(1);
$booking = Booking::create([ // <- using Booking::create
'home_id' => $home->id // <- using Booking::create
]); // <- using Booking::create
$booking->guests()->attach($guestIds);
return response()->json([
'booking' => $booking,
]);
}

Get data from tables with one to one relation for API in Laravel

I'm working on my first Laravel project, and I want to create a REST Api for android application. In my system, I have two tables: categories and images. Table images has column category_id which is a foreign key that references column id on category table.
The categories table
//users table migration
class CreateCategoriessTable extends Migration
{
public function up()
{
Schema::create('categories', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->timestamps();
});
}
...
}
The images table
class CreateImagesTable extends Migration
{
public function up()
{
Schema::create('images', function(Blueprint $table){
$table->increments('id');
$table->string('name')
$table->integer('category_id')->unsigned();
$table->foreign('category_id')
->references('id')
->on('categories')
->onDelete('cascade');
$table->timestamps();
});
}
...
}
In the Images model class I did it:
class Images extends Model
{
protected $fillable = ['name'];
protected $hidden = array('created_at', 'updated_at');
public function category(){
$this->belongsTo('App\Category');
}
}
I also created CategoryResource() class as:
class CategoryResource extends JsonResource
{
public function toArray($request)
{
return [
'id'=> $this->id,
'name' => $this->name,
];
}
}
So, I created a CategoryController to with the API methods, and configured the routes to access the corresponding function. The api/category/ url via GET is redirect to the index function of my controller, and the function is like that:
public function index()
{
$categories = Category::get();
return CategoryResource::collection($categories);
}
With this, I can get the categories table data, but I would like merge the users and images table, and get something like this as response:
[
{
'id': 1,
'name': 'category_name',
'image': 'image_name'
}
]
How I can do this?
First add a hasOne relation in Category model for image like this
Category Model
public function image(){
return $this->hasOne('App\Image');
}
Now specify the relation in your CategoryResource
class CategoryResource extends JsonResource
{
public function toArray($request)
{
return [
'id'=> $this->id,
'name' => $this->name,
'image' => new ImageResource($this->whenLoaded('image'))
];
}
}
Create ImageResource for loading images
class ImageResource extends JsonResource
{
public function toArray($request)
{
return [
'id'=> $this->id,
'name' => $this->image_name,
];
}
}
Finally load images relations with eager load in your controller like this
public function index()
{
$categories = Category::with('image')->get();
return CategoryResource::collection($categories);
}

inserting data into database laravel 5.6 relation belongstomany pivot table

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

Error trying to output the Eloquent relationship in Laravel

I'm getting the error "Trying to get property 'company_name' of non-object". I studied about the Eloquent relationship and try to implement in my code. But it gives me that error in the view (products.show)
Which part are wrong?
Is it okay to have many different relationship to other model as well?
In 'Vendor Model':
public function getRouteKeyName()
{
return 'roc_no';
}
public function user()
{
return $this->belongsTo('App\User');
}
public function products()
{
return $this->hasMany('App\Product');
}
In 'Product Model':
public function getRouteKeyName()
{
return 'slug';
}
public function user()
{
return $this->belongsTo('App\User');
}
public function vendor()
{
return $this->belongsTo('App\Vendor');
}
In 'User Model':
public function vendor()
{
return $this->hasOne('App\Vendor');
}
public function products()
{
return $this->hasMany('App\Product');
}
public function roles()
{
return $this->belongsToMany('App\Role', 'role_users');
}
In the 'products.show':
...
{!! $product->description !!}
<!-- The error is at $product->vendor->company_name -->
Company Name: {{ $product->vendor->company_name }}
In 'ProductController':
public function store(Request $request)
{
$this->validate($request, [
'name' => 'required|string|max:255',
'slug' => 'required|string|max:100',
'description' => 'required',
'image' => 'nullable',
]);
$product = new Product;
$product->name = $request->name;
$product->slug = $request->slug;
$product->description = $request->description;
$product->vendor_roc_no = auth()->user()->vendor->roc_no;
$product->save();
return redirect('/account/products')->with('success', 'Product added successfully.');
}
public function show(Product $product)
{
return view('products.show')->with('product', $product);
}
Updated:
In vendors table:
Schema::create('vendors', function (Blueprint $table) {
$table->increments('id');
$table->string('company_name');
$table->string('roc_no');
$table->string('company_address');
$table->string('company_tel');
$table->string('company_fax')->nullable();
$table->string('company_email');
$table->string('company_website')->nullable();
$table->string('company_logo')->nullable();
$table->text('company_profile')->nullable();
$table->unsignedInteger('user_id');
$table->timestamps();
$table->foreign('user_id')->references('id')->on('users');
});
In products table:
Schema::create('products', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('slug')->unique();
$table->text('description');
$table->string('image')->nullable();
$table->string('vendor_roc_no');
$table->timestamps();
// $table->foreign('vendor_id')->references('id')->on('vendors');
});
As far as I know, you'll need to establish the relationship for accessing properties from the other model, so in this case, you want to get the company_name that is on the vendor for this you'll need to tell your model to bring vendor. Example:
$user->with('anyRelation')->get();
// Then you can use like, $user->anyRelation->property;
Now I noticed something in your show method, you're sending Product class but not an eloquent object maybe just using Eloquent would help? Example
$products = Product:all(); // this would return every record on your db
return view('products.show')->with('products', $products);
I hope this helps :)

Categories