I have this SQL structure to query schools and the number of student of male gender,
I am asking for help for converting it to laravel eloquent
SELECT *
FROM schools && count(students has(gender == 'male'))
JOIN grades ON (grades.schools = schools.school_id)
JOIN streams ON (stream.schools = schools.school_id)
JOIN students ON (student.schools = schools.school_id)
this is what i did in the schemas
school schema
Schema::create('schools', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('educationLevel');
$table->foreignId('ward_id')
->constrained('wards')
->onUpdate('cascade');
$table->timestamps();
});
grade
Schema::create('grades', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->foreignId('school_id')->constrained('schools')
->onDelete('cascade');
$table->timestamps();});
stream
Schema::create('streams', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->foreignId('grade_id')
->constrained('grades')
->onDelete('cascade');
$table->timestamps();
});
student
Schema::create('students', function (Blueprint $table) {
$table->id();
$table->string('student_name');
$table->string('gender');
$table->foreignId('stream_id')
->constrained('streams')
->onDelete('cascade');
$table->timestamps();
});
this is what i tried before
in school controller
$schools = School::select(['name'])->withCount('students')->where('students', function($query){
$query->where('gender', 'male');
})
->get();
in school model i did this below
public function grades()
{
return $this->hasMany(Grade::class);
}
public function students(){
return $this->hasManyThrough(Student::class, Stream::class, Grade::class);
}
the relationship of this models is one to many like below
school->has->grade->has->stream->student(gender = male or female)
You can leverage addSelect to get the desired output
School::query()
->addSelect([
/** Total no of students in school */
'count_students' => Student::selectRaw('count(*)')
->whereIn(
'stream_id',
Stream::select('id')->whereIn(
'grade_id',
Grade::select('id')->whereColumn('school_id', 'schools.id')
)
),
/** Total no of "gender = male" students in school */
'count_male' => Student::selectRaw('count(*)')
->whereRaw('gender = "male"')
->whereIn(
'stream_id',
Stream::select('id')->whereIn(
'grade_id',
Grade::select('id')->whereColumn('school_id', 'schools.id')
)
),
/** Total no of "gender = female" students in school */
'count_female' => Student::selectRaw('count(*)')
->whereRaw('gender = "female"')
->whereIn(
'stream_id',
Stream::select('id')->whereIn(
'grade_id',
Grade::select('id')->whereColumn('school_id', 'schools.id')
)
),
/** Total no of "gender = other" students in school */
'count_other' => Student::selectRaw('count(*)')
->whereRaw('gender = "other"')
->whereIn(
'stream_id',
Stream::select('id')->whereIn(
'grade_id',
Grade::select('id')->whereColumn('school_id', 'schools.id')
)
)
])->get();
You would need something approaching this:
$schools = School::Join('grades', 'grades.schools', '=', 'schools.school_id')
->Join('stream', 'stream.schools', '=', 'schools.school_id')
->Join('student', 'student.schools', '=', 'schools.school_id')
->where('gender', "male")
->get([
'name','yourdata'
]);
To get the count of data, you simply calculate with count method.
$schoolCount = count($schools);
Make sure your table and column names are appropriate, I am seeing a lot of inconsistencies in your model and table names, same for columns. also, using join you might run into issues like columns with same name. make sure to specify them separately in the get method.Like:
->get([
'id', 'student.schools as stud', 'stream.name as streamName',
]);
Related
I have a many to many relationship between two tables : products and orders and the pivot is order_details
the pivot has two columns :
order_id reference on id in orders
sku reference on sku in products
I have defined the relation in the models of the tables like this :
Order Model
public function products()
{
return $this->belongsToMany(Product::class , 'order_details' , 'order_id' , 'sku');
}
Product Model
public function orders()
{
return $this->belongsToMany(Order::class , 'order_details' , 'sku' ,'order_id') ;
}
so when I try to reach the products from Order model I get an empty array even if there was product related to this order
order_details migration
public function up()
{
Schema::create('order_details', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('quantity');
$table->string('sku') ;
$table->foreign('sku')->references('sku')->on('products')->cascadeOnDelete() ;
$table->double('price') ;
$table->double('price_before_discount') ;
$table->foreignIdFor(Order::class)->constrained()->cascadeOnDelete();
$table->timestamps();
});
}
products migration
*
Schema::create('products', function (Blueprint $table) {
$table->id();
$table->double('price');
$table->unsignedBigInteger('quantity');
$table->integer('discount')->default(0);
$table->foreignIdFor(\App\Models\Brand::class)->constrained()->cascadeOnDelete();
$table->unsignedFloat('rating')->default(0);
$table->string('slug')->unique();
$table->string('sku');
$table->boolean('is_active')->default(1);
$table->boolean('is_available')->default(1);
$table->foreign('sku')->references('sku')->on('skus')->cascadeOnDelete();
$table->timestamps();
$table->softDeletes();
});
orders migration
Schema::create('orders', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('order_number')->unique();
$table->double('total');
$table->string('status')->default('pending');
$table->double('total_before_discount');
$table->string('invoice')->nullable();
$table->foreignIdFor(PromoCode::class)->constrained()->cascadeOnDelete();
$table->foreignIdFor(Customer::class)->constrained()->cascadeOnDelete();
$table->foreignIdFor(Address::class)->constrained()->cascadeOnDelete();
$table->timestamps();
});
Try this code hope it will help you
In Product Model:
public function orders()
{
return $this->belongsToMany(Order::class , 'order_details' , 'sku' ,'order_id' , 'sku' , 'id');
}
In Order Model
public function products()
{
return $this->belongsToMany(Product::class , 'order_details' , 'order_id' , 'sku' , 'id' , 'sku');
}
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]);
})
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 a polymorphic table called 'Item':
Schema::create('items', function(Blueprint $table) {
$table->increments('id');
$table->text('itemable_type')->nullable();
$table->string('itemable_id')->nullable();
$table->integer('subject_id')->nullable();
$table->integer('user_id')->nullable();
$table->integer('new')->nullable();
$table->integer('school')->nullable();
$table->string('private')->nullable();
$table->text('snippet')->nullable();
$table->string('elementSubject')->nullable();
$table->integer('follow_id')->nullable();
$table->timestamps();
});
Also have a table called 'Follow':
Schema::create('follows', function(Blueprint $table) {
$table->increments('id');
$table->integer('subject_id')->nullable();
$table->integer('teacher_id')->nullable();
$table->integer('user_id')->nullable();
$table->integer('effort')->nullable();
$table->timestamps();
});
Also have a table called 'Subject':
Schema::create('subjects', function(Blueprint $table) {
$table->increments('id');
$table->string('name')->nullable();
$table->integer('studentID')->nullable();
$table->integer('teacherID')->nullable();
$table->integer('schoolID')->nullable();
$table->integer('memberID')->nullable();
$table->string('private')->nullable();
$table->integer('creator')->nullable();
$table->softDeletes();
$table->timestamps();
});
They are linked together like this in my model:
class Follow extends Eloquent {
protected $guarded = array();
public static $rules = array();
public function subjects()
{
return $this->hasMany('Subject');
}
I like to filter the 'Item' list based on the 'Subjects' the 'User' is following. What would my Eloquent look like?
I have this filtering on the 'School' but I want to filter it on what the user is 'Following'
$follows = Follow::where('user_id', Auth::user()->id)->get();
$items = Item::where('school', Auth::user()->school)
->where('private', 'false')
->orderBy('created_at', 'DESC')
->get();
I have 2 tables: 'users' and 'nests' and 1 pivot table: 'nest_user'. In my pivot table I have email addresses I want to filter against so I can grab all the nests which have the associated email addreses. Here is my scehma:
public function up()
{
Schema::create('users', function(Blueprint $table)
{
$table->increments('id');
$table->string('username');
$table->text('bio');
$table->string('picture');
$table->string('email');
$table->string('password');
$table->integer('visits');
$table->integer('nest_id');
$table->timestamps();
});
}
public function up()
{
Schema::create('nests', function(Blueprint $table)
{
$table->increments('id');
$table->string('name');
$table->string('info');
$table->integer('note_id');
$table->integer('website_id');
$table->integer('image_id');
$table->integer('video_id');
$table->integer('location_id');
$table->integer('user_id');
$table->integer('share_id');
$table->string('inviteAuth');
$table->string('tid');
$table->timestamps();
});
}
public function up()
{
Schema::create('nest_user', function(Blueprint $table)
{
$table->increments('id');
$table->integer('user_id');
$table->integer('nest_id');
$table->string('inviteEmail');
$table->timestamps();
});
}
I can do this no problem based on the User ID like this:
Route::get('/t1', function () {
$nest = User::find(2)->nest()->where('inviteEmail', '=', 'martinelli#gmail.com')->get();
foreach( $nest as $nest)
echo $nest->name, ': ', $nest->pivot->inviteEmail, "</br>";
});
I can get the nest and name and the email in the pivot - awesome...However, I want to find ALL the 'nests' which have the associated email which is NOT tied to a user ID. This is getting me closer but it is still not working:
Route::get('/t4', function () {
$users = User::all();
foreach($users as $users)
{
$nests = $users->with(array('nest' => function($query) {
$query->where('inviteEmail', '=', 'martinelli#gmail.com');
}))->get();
foreach($nests->nest as $nest)
{
echo $nest->name,"<br />";
}
}
});
I am getting this error:
Undefined property: Illuminate\Database\Eloquent\Collection::$nest
I'm not sure I fully understand your question, but your last code block doesn't make sense. You'll want to do the with while fetching the users. Also, the error you're getting makes sense, because the $nests (Collection) doesn't have a $nest property.
Again, I'm not sure this is what you're after, but give this a try:
Route::get('/t4', function () {
// Get all users and their nests where nests are filtered by inviteEmail
$users = User::with(array('nest' => function($query) {
$query->where('inviteEmail', '=', 'martinelli#gmail.com');
}))->get();
// Loop through all users
foreach($users as $user)
{
// I'm asuming you defined the relation as nests() in your User model.
foreach($user->nests as $nest)
{
echo $nest->name . "<br />";
}
}
});