Assign User to any Shop in Laravel Relationship? - php

In laravel, I have 3 table
User // for Authentication and Create Another User Once logged in
Expense
Shop
My Purpose- I want user can register and Also, can create another user when they logged in And Can assign user to another Shop as they want..
And Only User in the Same Shop Can see their Expense..
// My User Table
<pre>
Schema::create('users', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('shop_id')->nullable();
$table->unsignedBigInteger('user_id')->nullable();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
</pre>
// My Expense Table
<pre>
Schema::create('expenses', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('user_id');
$table->date('date');
$table->string('description');
$table->double('amount');
$table->timestamps();
$table->foreign('user_id')->references('id')->on('users');
});
</pre>
// My Shop Table
<pre>
Schema::create('shops', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('expense_id')->nullable();
$table->unsignedBigInteger('user_id');
$table->string('name');
$table->string('description');
$table->timestamps();
$table->foreign('expense_id')->references('id')->on('expenses');
$table->foreign('user_id')->references('id')->on('users');
});
</pre>
// My User Model
<pre>
public function expense()
{
return $this->hasMany(\App\Expense::class);
}
public function shop()
{
return $this->hasMany(\App\Shop::class, 'user_id');
}
</pre>
// My Expense Model
<pre>
class Expense extends Model
{
protected $fillable = ['date', 'description', 'amount', 'user_id', 'shop_id'];
public function user()
{
return $this->belongsTo(\App\User::class);
}
}
</pre>
// My Shop Model
<pre>
class Shop extends Model
{
protected $fillable = ['name', 'description', 'expense_id', 'shop_id'];
public function user()
{
return $this->belongsTo(\App\User::class, 'user_id');
}
}
</pre>
// Expense Controller
<pre>
public function index(Request $request)
{
$expense = Expense::with(['user'])->get();
return ExpenseResource::collection($expense);
// dd(auth()->user());
}
public function create(Request $request)
{
$request->validate([
'date' => 'required',
'description' => 'required',
'amount' => 'required',
]);
$expense = new Expense();
$expense->user_id = auth()->user()->id;
$expense->date = $request->date;
$expense->description = $request->description;
$expense->amount = $request->amount;
$expense->save();
return new ExpenseResource($expense);
}
</pre>
// in My UserController
<pre>
public function index()
{
$users = User::all();
$shops = Shop::all();
return view('user', compact('users', 'shops'));
// return UserResource::collection($users);
}
public function create(Request $request)
{
$request->validate([
'name' => 'required',
'email' => 'required',
'password' => 'required',
]);
$user = new user();
$user->user_id = auth()->user()->id;
$user->name = $request->name;
$user->email = $request->email;
$user->password = bcrypt($request->password);
$user->save();
return new UserResource($user);
}
</pre>
Is it make sense?
Any idea, thanks..

As stated in the comments, you'll need to check the current User and constrain the returned Expense records to only those that 1) have a User and 2) match the same Store as the current User. This can be done in a single whereHas() clause:
public function index(Request $request) {
$user = auth()->user(); // If using default `Auth` logic.
$expenses = Expense::whereHas('user', function($subQuery) use($user){
return $subQuery->where('shop_id', '=', $user->shop_id);
})->with(['user'])->get();
return ExpenseResource::collection($expenses);
}
What ->whereHas() does is constrains the query fetching your Expense models to respect the logic you pass it, which in this case is only include Expense models that have a user that has the same shop_id as the currently logged in User.
Note:If the current User does not have a Shop, it might return unexpected results, but you could protect the route to only allow a User with a Shop to access it, etc.

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

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

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 :)

Polymorphic relation - morphTo save() not recognizing custom primary key

User model:
public function userable()
{
return $this->morphTo();
}
Mentor model:
public function user()
{
return $this->morphOne('App\Models\User', 'userable');
}
The student model looks the same as the mentor model.
The tables for Students and Mentors contain a custom PK called user_id which references the primary key on the users table.
So what I'm trying to do is the following:
$user = new User();
$user->first_name = 'Trajce';
$user->last_name = 'Petkoski';
$user->nickname = 'ads';
$user->administrator = '0';
$user->email = 'asd';
$user->password = Hash::make('test');
$user->save();
$mentor = new Mentor();
$mentor->user_id = $user->id;
$mentor->save();
$user->userable_id = $mentor->user_id;
$mentor->user()->save($user);
However, on the Users table, userable_id is being set to 0 while userable_type value is set to the corret value. The issue here is that save() sets it to a predefined 0. Any idea what's going on behind the scenes?
Try this to add data to Polymorphic relation (morphOne):
Migrations:
// User
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->string('first_name');
$table->string('last_name');
$table->string('nickname');
$table->integer('administrator');
$table->string('email');
// add these two for the relation to work
$table->integer('userable_id')->unsigned();
$table->string('userable_type');
//
$table->rememberToken();
$table->timestamps();
});
// Mentor
Schema::create('mentors', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
});
User Model
public function userable()
{
return $this->morphTo();
}
Mentor Model
public function user()
{
return $this->morphOne('App\Models\User', 'userable');
}
Association Code:
$mentor = new Mentor();
// this is important: first save mentor
$mentor->save();
$userdata = [
'first_name' => 'Trajce',
'last_name' => 'Petkoski',
'nickname' => 'ads',
'administrator' => 0,
'email' => 'asd',
'password' => Hash::make('test')
];
$mentor->user()->create($userdata);
This works like a charm in my Laravel 5.4 test installation
Try this
public function users() {
return $this->morphMany('App\Models\User', 'userable');
}

Categories