I currently have the following set up:
Category, SubCategory, Business, Enhancement & Product models.
A Category has many SubCategorys
A SubCategory has many Businesss
A Business may have one Enhancement
A Enhancement must have one Product
A product could be an enhanced listing, a premium listing, or additional features.
If the business has brought either an enhanced or premium listing, it is recorded in the enhancement table.
So, product#1 = Premium Listing. Product#2 = Enhanced listing.
What I am wanting is to return all the businesses for a chosen SubCategory, grouping by the product that the enhancement is attached to. If the business has an enhancement, I'm wanting to randomise the order. If the business does not have a product, I'm wanting to order by name in ASC order.
So the result I'm expecting is to have a complete list of all businesses, with those with a Premium Listing, listed first in a random order. Then all businesses with an enhanced listing in a random order. Then all the other businesses that have no enhancement, in alphabetical order.
I currently have all the businesses listed in name order with the following code:
SubCategory::where('slug', $subCategory)->where('category_id', $category->id)
->with(['businesses' => function($query) {
$query->orderBy('name', 'ASC');
}])->firstOrFail();
Category model:
class Category extends Model
{
public function subCategories()
{
return $this->hasMany('\App\SubCategory');
}
}
SubCategory model:
class SubCategory extends Model
{
public function businesses()
{
return $this->hasMany('\App\Business');
}
public function category()
{
return $this->belongsTo('\App\Category');
}
}
Business model:
class Business extends Model
{
public function subCategory()
{
return $this->belongsTo('\App\SubCategory');
}
public function enhancements()
{
return $this->hasMany('\App\Enhancement');
}
}
Enhancement model:
class Enhancement extends Model
{
public function business()
{
return $this->belongsTo('\App\Business');
}
public function product()
{
return $this->belongsTo('\App\Product');
}
}
Product model:
class Product extends Model { }
Migrations:
Schema::create('categories', function (Blueprint $table) {
$table->increments('id');
$table->string('name')->unique();
$table->string('slug')->unique();
$table->string('featured_image');
$table->text('description');
$table->unsignedInteger('sort_order')->nullable();
$table->timestamps();
$table->softDeletes();
});
Schema::create('sub_categories', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('category_id');
$table->string('name')->unique();
$table->string('slug')->unique();
$table->string('featured_image');
$table->text('description')->nullable();
$table->unsignedInteger('sort_order')->nullable();
$table->timestamps();
$table->softDeletes();
$table->foreign('category_id')->references('id')->on('categories');
});
Schema::create('businesses', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('resort_id');
$table->unsignedInteger('sub_category_id');
$table->string('name');
$table->string('slug')->unique();
$table->string('logo')->nullable();
$table->text('description');
$table->text('content')->nullable();
$table->string('county')->nullable();
$table->string('postcode')->nullable();
$table->timestamps();
$table->foreign('resort_id')->references('id')->on('resorts');
$table->foreign('sub_category_id')->references('id')->on('sub_categories');
});
Schema::create('enhancements', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('business_id');
$table->unsignedInteger('product_id');
$table->dateTime('from_date');
$table->dateTime('to_date');
$table->timestamps();
$table->softDeletes();
$table->foreign('business_id')->references('id')->on('businesses');
$table->foreign('product_id')->references('id')->on('products');
});
Schema::create('products', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('slug')->unique();
$table->string('image');
$table->text('description');
$table->float('yearly_price');
$table->float('monthly_price');
$table->timestamps();
});
This should work for you:
SubCategory::where('slug', $subCategory)
->where('category_id', $category->id)
->with(['businesses' => function($query) {
$query->select('businesses.*')
->leftJoin('enhancements', function($join) {
$join->on('enhancements.business_id', '=', 'businesses.id')
->join('products', 'products.id', 'enhancements.product_id');
})
->orderByRaw('FIELD(products.slug, ?, ?) DESC', ['enhanced-listing', 'premium-listing'])
->orderByRaw('CASE WHEN products.slug IS NOT NULL THEN RAND() ELSE businesses.name END');
}])->firstOrFail();
Related
If I have two tables individuals and contracts with a many to many relationship represented in a table contracts_individuals_map, how would make query that is equivalent to:
SELECT *
FROM individuals
WHERE individuals.id IN
(SELECT individual_id
FROM contracts_individuals_map
WHERE contract_id IN (9,11)
);
MODELS:
class Individual extends Model
{
public function contracts()
{
return $this->belongsToMany(Contract::class, 'contracts_individuals_map');
}
}
class Contract extends Model
{
public function individuals()
{
return $this->belongsToMany(Individual::class, 'contracts_individuals_map');
}
}
MIGRATION:
public function up()
{
Schema::create('contracts_individuals_map', function (Blueprint $table) {
$table->id();
$table->integer('contract_id')->unsigned();
$table->integer('individual_id')->unsigned();
});
Schema::table('contracts_individuals_map', function (Blueprint $table)
{
$table->foreign('contract_id')
->references('id')
->on('contracts')
->onUpdate('cascade')
->onDelete('cascade');
$table->foreign('individual_id')
->references('id')
->on('individuals')
->onUpdate('cascade')
->onDelete('cascade');
});
}
or more generally, if I had a variable of objects
$cts = Contract::whereIn('id', [9,11])->get()`;
How would I get get all individuals associated with all the contracts in $cts?
You can leverage whereHas with something like this:
$individuals = Individual::whereHas('contracts', function ($query) {
return $query->whereIn('contracts_individuals_map.contract_id', [9, 11]);
})
I'm trying to use two different Category model to the same items table.
I'v got 3 models
SystemCategory
Schema::create('system_categories', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->timestamps();
});
UserCategory
Schema::create('user_categories', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->integer('user_id');
$table->timestamps();
});
Item
Schema::create('items', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->integer('categoryable_id');
$table->string('categoryable_type');
$table->timestamps();
});
Item category could be either from system_categories or user_categories table
I saw some Polymorphic relations but its about how two different models can belongs to one category, not about how model can belongs to two different category models
Thanks.
It can be done, first thing your schema looks ok but you want to set catagoryable_id to a bigInteger to match the id columns of the 2 category tables.
Then you would set your models up
class Item extends Model
{
public function categoryable()
{
return $this->morphTo();
}
}
class SystemCategory extends Model
{
public function items()
{
return $this->morphMany('App\Item', 'categoryable');
}
}
class UserCategory extends Model
{
public function items()
{
return $this->morphMany('App\Item', 'categoryable');
}
}
Obviously this is presuming your models are in the App namespace
I'm experiencing the following:
Goal: I would like to list all partners and the categories they belong to. Basically, it's a many to many relationship.
Below is the code:
Partner Categories Table Migration
public function up()
{
Schema::create('partcats', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('partcatnameP')->unique();
$table->unsignedBigInteger('user_id')->unsigned();
$table->foreign('user_id')->references('id')->on('users');
$table->timestamps();
});
}
Partners Table Migration
public function up()
{
Schema::create('partners', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('partnername');
$table->string('regnumber')->unique();
$table->unsignedBigInteger('activestatus_id')->unsigned();
$table->foreign('activestatus_id')->references('id')->on('activestatuses');
$table->unsignedBigInteger('user_id')->unsigned();
$table->foreign('user_id')->references('id')->on('users');
$table->timestamps();
});
}
Partner_Category Migration
public function up()
{
Schema::create('partner_partcat', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('partner_id')->unsigned();
$table->foreign('partner_id')->references('id')->on('partners')->onDelete('cascade');
$table->unsignedBigInteger('partcat_id')->unsigned();
$table->foreign('partcat_id')->references('id')->on('partcats')->onDelete('cascade');
$table->timestamps();
});
}
Models are as shown below:
Partcat Model
public function partners()
{
return $this->belongsToMany('App\Partcat','partner_partcat');
}
Partner Model
public function partcats()
{
return $this->belongsToMany('App\Partcat','partner_partcat');
}
and the Partners Controller is as shown below:
public function index()
{
//
$partners = Partner::all()->partcats();
// dd(Partner::all()->partcats());
return view('partners.index',['partners'=>$partners]);
}
This is where I'm trying to retrieve the list of partners and its related categories. However, I get a BadMethod call error.
You can use the following method
$partners = Partner::with('partcats')->get();
https://laravel.com/docs/5.6/eloquent-relationships#eager-loading
A many to many pivot table typically has the id for both tables it is relating.
The Partner_Category migration you have posted only seems to contain a partner_id. You may need to add in the category_id.
public function up()
{
Schema::create('partner_partcat', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('partner_id')->unsigned();
$table->unsignedBigInteger('partcat_id')->unsigned();
$table->timestamps();
$table->foreign('partner_id')->references('id')->on('partners')->onDelete('cascade');
$table->foreign('partcat_id')->references('id')->on('partcats')->onDelete('cascade');
});
}
Hey guys In a new project for a client I have to make a filtration system for books that have genres.
I've made a many to many relationship and a pivot table. So far I've made the form with check boxes, because the goal is to query a combination of two or more genres and show only the results that have all the selected genres.
My migrations are:
Schema::create('genres', function (Blueprint $table) {
$table->increments('id');
$table->string('name', 40);
$table->timestamps();
});
Schema::create('books', function (Blueprint $table) {
$table->increments('id');
$table->string('name', 125);
$table->timestamps();
});
Schema::create('book_genre', function (Blueprint $table) {
$table->increments('id');
$table->integer('genre_id')->unsigned();
$table->foreign('genre_id')->references('id')->on('genres')->onDelete('cascade');
$table->integer('book_id')->unsigned();
$table->foreign('book_id')->references('id')->on('books')->onDelete('cascade');
$table->timestamps();
});
My model is:
class Book extends Model{
public function genres(){
return $this->belongsToMany('App\Genre', 'book_genre');
}
}
This is my controller where I take the Get array with genre IDs:
class FilterController extends Controller
{
public function index()
{
$genreIds = Input::get('genres');
$books = Book::whereHas('genres', function($q) use ($genreIds){$q->whereIn('genres.id', $genreIds);})->orderBy('created_at', 'desc')->paginate(10);
}
return view('filter.index', compact('books'));
}
Here the problem is that it is not filtering the results by a combination of the genres, it is filtering them simply if they have one of the genres.
What should I change so I can query out only the matching books what have the listed genres?
$query = Book::with('genres');
foreach($genreIds as $genreId){
$query->whereHas('genres', function($q) use ($genreId){
$q->where('id', $genreId);
});
}
$filteredBook = $query->get();
I found the same solution in Stackoverflow
I have two models Post and Category
// migration post
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->increments('id');
$table->string('title');
$table->string('body');
$table->string('image');
$table->integer('category_id')->unsigned();
$table->foreign('category_id')->references('id')->on('categories');
$table->timestamps();
});
}
// migration category
public function up()
{
Schema::create('categories', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->timestamps();
});
}
How can I display only the last post from each category in home page?
Hiren was close, but you need to go from the category since your post is owned by the category
$category->posts()->latest()->first();
Alternatively you could work backwards:
$post = Post::latest()->whereHas('category', function($q) use($category_id) {
return $q->where('id', $category_id);
})->first();
For this to work you'll need to define your model relationships:
Category Model needs this function:
public function posts()
{
return $this->hasMany(App\Post::class);
}
Post Model needs this function:
public function category()
{
return $this->belongsTo(App\Category::class);
}
To respond to Alexey Mezenin, we can just pass a callback to with() to define which posts we want to pull in for each category, performing the correct eager load.
Category::with(['posts' => function($q) {
return $q->latest()->first();
})->get();
An Eloquent solution for loading categories with latest post is to create an additional hasOne() relationship in the Category model:
public function latestPost()
{
return $this->hasOne(Post::class)->latest();
}
And then use eager loading:
Category::with('latestPost')->get();
This will generate just 2 queries to DB.
public function up()
{
Schema::create('news', function (Blueprint $table) {
$table->increments('id');
$table->string('slug')->unique();
$table->unsignedInteger('author_id');
$table->unsignedInteger('category_id');
$table->string('subject');
$table->text('short');
$table->text('content');
$table->integer('view')->default(0);
$table->integer('status')->default(0);
$table->string('image');
$table->timestamps();
$table->foreign('author_id')
->references('id')->on('users')
->onDelete('cascade');
// $table->foreign('category_id')
// ->references('id')->on('categories')
// ->onDelete('cascade');
});
// Schema::enableForeignKeyConstraints();
}
public function up()
{
Schema::create('categories', function (Blueprint $table) {
$table->increments('id');
$table->string('name')->unique();
$table->timestamps();
});
}
in contoller:
$latestpostlist = News::whereIn('created_at',function($query){
$query->select(DB::raw('max(created_at)'))
->from('news')
->groupBy('category_id');
})->get();
in your case news will be post. this query worked for me