GroupBy results from a HasManyThrough a ManyToMany relationship in Laravel 5.5 - mysql

I have a complex relationship like a ManyToMany relationship between Category and Products and a Product can have many Inventory listings from different Shops(user). A Shop can have multiple listing of the same Product as a variant(color/size).
Everything is fine till now!
The issue begins when showing the listing to the visitors. I want to show all listings under a category but don't want to show multiple listings of the same product from the same shop. Instead, want to pick the lowest sale_price listing from the shop.
I have three models and relations are like:
class Category extends Model
{
public function products()
{
return $this->belongsToMany(Product::class);
}
}
class Product extends Model
{
public function categories()
{
return $this->belongsToMany(Category::class);
}
public function inventories()
{
return $this->hasMany(Inventory::class);
}
}
class Inventory extends Model
{
public function product()
{
return $this->belongsTo(Product::class);
}
}
Tables:
//Categories
Schema::create('categories', function (Blueprint $table) {
$table->increments('id');
$table->string('name')->unique();
});
//Products
Schema::create('products', function (Blueprint $table) {
$table->increments('id');
$table->string('name')->unique();
});
//Pivot table
Schema::create('category_product', function (Blueprint $table) {
$table->integer('category_id');
$table->integer('product_id');
});
//Inventories
Schema::create('inventories', function (Blueprint $table) {
$table->increments('id');
$table->integer('shop_id');
$table->integer('product_id');
$table->string('sku', 200);
$table->string('title');
$table->decimal('sale_price', 10, 2);
});
As Laravel doesn't provide any manyToManyThough() relationship. I have added a listings method in the Category model. This method returns all listings:
public function listings()
{
$product_ids = $this->products()->pluck('products.id')->all();
return Inventory::whereIn('product_id', $product_ids)->paginate(20);
}
Then I tried this:
public function listings()
{
$product_ids = $this->products()->pluck('products.id')->all();
return Inventory::whereIn('product_id', $product_ids)->groupBy('shop_id','product_id')->paginate(20);
}
These methods produce MySQL GroupBy Error. Yes, I can filter the result after taking all result but that'll affect the pagination. How to get the sorted results and also keep the pagination ability. Thanks and day by day I got more indebted to the community. :)

You can create a direct relationship by "skipping" the products table:
public function listings() {
return $this->belongsToMany(
Inventory::class,
'category_product',
null,
'product_id',
null,
'product_id'
);
}

Related

How to Display Data from Table relationship base on ID come from first table LARAVEL

I have 3 table, table Courses, table Sections, and table Syllabuses. Where table courses relation to table Sections and table Sections relation to table Syllabuses. To make clear below my table look like:
#Table Courses:
public function up()
{
Schema::create('courses', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('user_id');
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade')->onUpdate('cascade');
$table->unsignedBigInteger('category_id')->default('1');
$table->foreign('category_id')->references('id')->on('categories')->onDelete('cascade')->onUpdate('cascade');
$table->string('title');
$table->string('status')->default('publish');
$table->text('content')->nullable();
$table->timestamps();
});
}
Table Sections:
public function up()
{
Schema::create('sections', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('course_id');
$table->foreign('course_id')->references('id')->on('courses');
$table->string('name');
$table->timestamps();
});
}
and the last table Syllabuses:
public function up()
{
Schema::create('syllabuses', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('section_id');
$table->foreign('section_id')->references('id')->on('sections')->onDelete('cascade')->onUpdate('cascade');
$table->string('title')->nullable();
$table->text('content')->nullable();
$table->text('description')->nullable();
$table->timestamps();
});
}
and now I'm stuck to display data from table syllabuses base on Sections's ID, where the sections display base on Courses's ID, because I don't know how to get the ID from each row which related to Secations's ID to put in Syllabus::where('section_id',???)->get();. Anyone who can help me much much appreciate. Hope u understand what I mean.
Below the image you can see, there I have put some note to make u understand. The problem in the green box one.
enter image description here
Define relations in your models first.
I assume your models are: Course, Section and Syllabus
// Model Course
public function sections()
{
return $this->hasMany(Section::class);
}
// Model Section
public function course()
{
return $this->belongsTo(Course::class);
}
public function syllabuses()
{
return $this->hasMany(Syllabus::class);
}
// Model Syllabus
public function section()
{
return $this->belongsTo(Section::class);
}
Now in your controller you can get your designated course by
// Controller Course
public function show($id)
{
$course = Course::with('sections', 'sections.syllabuses')->where('id', $id)->first();
}
In this query you have a model from "Course" table, related models from "Sections" table and all the related models from "Syllabuses" table. If you are using laravel's blade then you can simply access them by:
// course
{!! $course->your_attribute !!}
// to access sections
#foreach($course->sections as $section)
#endforeach
// to access syllabuses
#foreach($course->sections as $section)
#foreach($section->syllabuses as $syllabus)
// Here you will have syllabuses for individual sections
#endforeach
#endforeach
https://laravel.com/docs/8.x/eloquent-relationships#one-to-many
You need to study laravel relationships in your case you have belongsTo relation.
In your Syllabus model add.
public function section(){
return $this->belongsTo(Section::class, 'section_id');
}
Then you can use this will get all Syllabuses with related to there sections.
Syllabus::with('section')->get();

Laravel One To Many (Inverse) / Belongs To return null

I use Laravel 8. I have 3 table. Course table, UserCourse, and User table. I want to get user courses. I tried it with tinker: Course::find(1)->user_courses -it works fine and give back me user_course.
UserCourse::find(1)->user_course - the problem is here, this will return me "null"
How can I get the User Courses?
Course table
Schema::create('courses', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->timestamps();
});
UserCourse table
Schema::create('user_courses', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->onDelete('cascade');
$table->foreignId('course_id')->constrained()->onDelete('cascade');
$table->timestamps();
});
User table
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('email')->unique();
$table->string('username')->unique();
$table->string('password');
$table->timestamp('updated_at')->nullable();
$table->timestamp('created_at')->nullable();
});
Course.php
class Course extends Model
{
use HasFactory;
protected $fillable = [
'title',
];
public function user_courses()
{
return $this->hasMany(UserCourse::class);
}
}
UserCourse.php
class UserCourse extends Model
{
use HasFactory;
protected $fillable = [];
public function user_course()
{
return $this->belongsTo(Course::class);
}
}
Your database structure make it that the relation between course and user is a many to many relation with a pivot table in the middle.
Some correction you need to do for it to work seemingly with laravel conventions.
- The pivot table name should be course_user without a primary key
Schema::create('course_user', function (Blueprint $table) {
$table->foreignId('user_id')->constrained()->onDelete('cascade');
$table->foreignId('course_id')->constrained()->onDelete('cascade');
$table->timestamps();
});
- Remove the UserCourse.php model since it's a pivot
- Define the relation courses in User model as follow
User.php
public function courses()
{
return $this->belongsToMany(Course::class);
}
- Define the relation users in Course model as follow
Course.php
public function users()
{
return $this->belongsToMany(User::class);
}
Now to get any user courses, just run User::find(1)->courses
Same thing to get the users that belongs to the same couses Course::find(1)->users
get courses that the user has not taken:
$userId = 1;
$courses = Course::whereDoesntHave('users', function($userQB) use($userId) {
$userQB->where('id',$userId);
})->get();

How to delete a user and automatically all his relations using 'cascade'?

I'm trying to delete a user and automatically all his relations using 'cascade'. Not sure how to do it. I'm using mysql.
So far i've made this:
User Model
class User extends Authenticatable
{
use Notifiable;
//...
public function profile()
{
return $this->hasOne('App\Profile');
}
}
Profile Model
class Profile extends Model
{
public function user()
{
return $this->belongsTo('App\User');
}
}
Profile migration
public function up()
{
Schema::create('profiles', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('user_id');
$table->integer('phone');
$table->timestamps();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
});
}
And with tinker I'm tryng to do:
$user = App\User::firsts();
$user->delete(); // it deletes only the user
$user->profile->delete(); // it deletes only the profile
You could make use of Model Events:
class User extends Eloquent
{
public static function boot ()
{
parent::boot();
self::deleting(function ($user) {
// This will be executed right before the user is deleted
$user->profile->delete();
});
}
}
This way, whenever you call the delete() method on a User object, Laravel will fire the $user->profile->delete(); right before.
Rather than a database cascade you could delete the related model when the user is deleted by deleting the related model using the deleting event.
Include this in the Boot function of the User Model:
public static function boot ()
{
parent::boot();
self::deleting(function (User $user) {
$user->profile->delete();
});
}
Theres no need to do relationship staff for working with cascade. This code works fine for me -
Migration:
Schema::create('profiles', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('user_id');
$table->integer('phone');
$table->timestamps();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
});
Controller:
Here ->delete() can delete both user and profile row
public function deleteUser(){
Auth::user()->delete();
return redirect()->back();
}
View Delete Link
Delete
Route
Route::get('/delete', 'HomeController#deleteUser')->name('deleteUser');

Laravel 5.1 manyToMany with two classes and third in common

Here is the example:
Two classes: chairs and tables. Both of those classes are connected with third class colours with ManyToMany relations.
Let's say I have element of chairs class with two colours: red and blue.
How do I get matching tables, the tables that also belong to red and blue?
EDIT:
Chair model:
class Chair extends Model
{
public function colours()
{
return $this->belongsToMany('App\Colour')->withTimestamps();
}
}
Table model:
class Table extends Model
{
public function colours()
{
return $this->belongsToMany('App\Colour')->withTimestamps();
}
}
Colors model:
class Colour extends Model
{
public function chairs()
{
return $this->belongsToMany('App\Chair')->withTimestamps();
}
public function tables()
{
return $this->belongsToMany('App\Tables')->withTimestamps();
}
}
Chair migration's up function:
public function up()
{
Schema::create('chairs', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->string('name');
});
}
Chair-Colour pivot table:
public function up()
{
Schema::create('chair_colour', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->integer('chair_id')->unsigned();
// $table->foreign('chair_id')->references('id')->on('chairs')->onDelete('cascade');
$table->integer('colour_id')->unsigned();
// $table->foreign('colour_id')->references('id')->on('colours')->onDelete('cascade');
});
}
Remaining migrations (tables, chair_table, colours) are the same.
Foreign key constrains are commented out because I had problems when refreshing migrations with them included.
This is just manyToMany relation but I hope it helps. I just made this up since my code is more complicated so if You see any typos feel free to correct them or notify me in comments.
Thank You!

Eloquent - Many To Many, users-topics delete all topics by user id

Given Eloquent Many To Many relationship:
Migration:
class Topics extends Migration
{
public function up()
{
Schema::create('topics', function(Blueprint $table) {
$table->increments('id');
$table->string('name');
});
Schema::create('topic_user', function(Blueprint $table) {
$table->integer('user_id')->unsigned();
$table->foreign('user_id')->references('id')->on('users');
$table->integer('topic_id')->unsigned();
$table->foreign('topic_id')->references('id')->on('topics');
});
}
}
Model:
class User extends Model
{
public function topics()
{
return $this->belongsToMany('App\Topic', 'topic_user');
}
}
I'm wondering how can I delete all topics by given user, currently my code looks like this:
Auth::user()->topics()->delete();
But I'm getting exception:
ERROR: missing FROM-clause entry for table "topic_user" LINE 1: delete
from "topics" where "topic_user"."user_id" = $1 ^ (SQL: delete from
"topics" where "topic_user"."user_id" = 6)
What I'm doing wrong?
That's pretty simple:
Auth::user()->topics()->detach();