Laravel 5.4 one to many relationship - laravel-5.4

I have two tables in my model:
- Groups (id, name, category_id)
- Categories (id, name)
I would need to show in my view a list with every group name and its corresponding category. Could you please show me how to populate the Eloquent method and also what do I need in the view side?
This is what I have so far but of course is not working
-- GroupController
` public function index()
{
$groups = Category::find(1);
return view('groups.groups',compact('groups'));
}
-- Group
public function category()
{
return $this->belongsTo('App\Category');
}
-- CategoryController
` public function groups()
{
return $this->hasMany('App\Group');
}`
-- Category
` public function groups()
{
return $this->hasMany('App\Group');
}`
-- view
#foreach($groups as $group)
{{$group->category}}
{{$group->category->name}}
#endforeach
thanks!!

Following up on my comments, what you want is the following:
Models + relations:
Group model
public function category()
{
return $this->belongsTo(Category::class);
}
Category model
public function groups()
{
return $this->hasMany(Group::class);
}
Then in your Group controller:
public function index()
{
// consider using paginate() instead of get()
// using with('category') to eagerload the category (1 extra query) instead of querying for the category in the views foreach loop.
$groups = Group::with('category')->get();
return view('groups.groups',compact('groups'));
}
And in your view you can now list the groups and their category:
#foreach ($groups as $group)
{{-- Assuming group and category have a name and category is always set --}}
{{ $group->name }} : {{ $group->category->name }}
#endforeach

An easy approach would be using Eloquent's Eager Loading:
https://laravel.com/docs/4.2/eloquent#querying-relations
Group::with('category')->get()
For example:
# Loop through all groups
foreach(Group::with('category')->get() as $group){
#get group name
print $group->name;
#get all categories related to this group
$categories = $group->category;
foreach ($categories as $category){
print $category->name;
}
print "<br>";
}

I finally got it.
In the GroupsController added this
´
$groups = Group::with('category')->get();
return view('groups.groups',compact('groups'));
´
and in the view:
#foreach($groups as $group) {{$group->category->name}}
thanks for your help

Related

Group by and Sum of One-To-Many relation tables in Eloquent

I have a requirement.
My DB has tables like the following.
The tables have OneToMany (1-n) parent-child relation.
Table School (id, school_name)
Table Class (id, school_id, class_name)
Table Section (id, class_id, section_name, no_of_seats)
Table Student (id, section_id, student_name, ....)
When Some Student is registered, data is uploaded to the Student table.
Now, I want to have a statistic like
| school_name | total_seats | student_registered |
and for a particular school
| class_name | total_seats | student_registered |
How to achieve this in Laravel/Eloquent
Thanks in Advance
Probably it works with:
Counting/Summarizing HasMany relations
Counting/Summarizing HasManyThrough relations
Counting/Summarizing HasManyDeep relations
Definition
class Section extends Model
{
public function students(): HasMany
{
return $this->hasMany(Student::class);
}
public function scopeWithRegisteredStudents(Builder $query): Builder
{
// Count HasMany relation
return $query->withCount('students as students_registered');
}
}
// The word "Class" is reserved, so we need to use "SchoolClass" instead
class SchoolClass extends Model
{
protected $table = 'classes';
public function sections(): HasMany
{
return $this->hasMany(Section::class, 'class_id');
}
public function students(): HasManyThrough
{
return $this->hasManyThrough(Student::class, Section::class, 'class_id');
}
public function scopeWithTotalSeats(Builder $query): Builder
{
// Summarize field from HasMany relation
return $query->withSum('sections as total_seats', 'no_of_seat');
}
public function scopeWithRegisteredStudents(Builder $query): Builder
{
// Count HasManyThrough relation
return $query->withCount('students as students_registered');
}
}
class School extends Model
{
public function classes(): HasMany
{
return $this->hasMany(SchoolClass::class);
}
public function sections(): HasMany
{
return $this->hasManyThrough(Section::class, SchoolClass::class, null, 'class_id');
}
public function students(): HasManyThrough
{
// https://github.com/staudenmeir/eloquent-has-many-deep
return $this->hasManyDeep(Student::class, [SchoolClass::class, Section::class], ['school_id', 'class_id', 'section_id'], ['id', 'id', 'id']);
}
public function scopeWithTotalSeats(Builder $query): Builder
{
// Summarize field from HasManyThrough relation
return $query->withSum('sections as total_seats', 'no_of_seat');
}
public function scopeWithRegisteredStudents(Builder $query): Builder
{
// Count HasManyDeep relation
return $query->withCount('students as students_registered');
}
}
Example
// Fetching simply
Section::query()
->withRegisteredStudents()
->get();
SchoolClass::query()
->withTotalSeats()
->withRegisteredStudents()
->get();
School::query()
->withTotalSeats()
->withRegisteredStudents()
->get();
// Fetching with nested relations
School::query()
->withTotalSeats()
->withRegisteredStudents()
->with(['classes' => function (HasMany $query) {
return $query
->withTotalSeats()
->withRegisteredStudents();
}])
->get();
If you use a static analyzer like PHPStan or Psalm, you can alternatively use scopes method to prevent errors.
School::query()
->scopes(['withTotalSeats', 'withRegisteredStudents'])
->get();
This is not what you asked for as it uses Query Builder instead of Eloquent. I have not tested it as I have nothing to test against currently but this should work -
use Illuminate\Support\Facades\DB;
$students_per_section = DB:table('students')
->select('section_id', DB::raw('COUNT(id) AS num_students'))
->groupBy('section_id')
$query = DB:table('schools')
->join('classes', 'schools'.'id', '=', 'classes.school_id')
->join('sections', 'classes.id', '=', 'sections.class_id')
->leftJoinSub($students_per_section, 'students_per_section', function($join) {
$join->on('sections.id', '=', 'students_per_section.section_id')
});
if ($school_id) {
$query
->select('classes.class_name', DB::raw('SUM(no_of_seats) AS total_seats'), DB::raw('SUM(students_per_section.num_students) AS student_registered'))
->where('schools.id', '=', $school_id)
->groupBy('classes.class_name')
} else {
$query
->select('schools.school_name', DB::raw('SUM(no_of_seats) AS total_seats'), DB::raw('SUM(students_per_section.num_students) AS student_registered'))
->groupBy('schools.school_name')
}
$stats = $query->get();

many-to-many relationship: order by on pivot table not working

I have these relationship between school and associate models:
// School model
public function associates()
{
return $this->belongsToMany('Associate', 'school_associate', 'school_id', 'associate_id')
->withPivot('start_date', 'end_date');
}
// Associate model
public function schools()
{
return $this->belongsToMany('School', 'school_associate', 'associate_id', 'school_id')
->withPivot('start_date', 'end_date');
}
I need to get all associates of one school ordered by start_date.
This is what I tried without success (in this try I am searching in all schools):
dd(\App\Associate::with(['schools' => function ($q) {
$q->orderBy('pivot_start_date', 'desc');
}])->toSql());
And I get this sql (notice no order by clause):
select * from `associate`
I tried to edit the relationship like this:
// Associate model
public function schools()
{
return $this->belongsToMany('School', 'school_associate', 'associate_id', 'school_id')
->withPivot('start_date', 'end_date')
->orderBy('pivot_start_date', 'desc'); // also tried without "pivot_"
}
And according to this post, I also tried :
// Associate model
public function schools()
{
return $this->belongsToMany('School', 'school_associate', 'associate_id', 'school_id')
->withPivot('start_date', 'end_date')
->orderBy('school_associate.start_date', 'desc');
}
But I always get the same query and the results are not ordered.
I solved using query builder in this way.
This function is in Associate model:
public function scopeLast($query, $school_ids = [])
{
$query->join('school_associate', "{$this->table}.{$this->primaryKey}", '=', 'school_associate.associate_id')
->join('school', 'school.school_id', '=', 'school_associate.school_id')
->whereIn('school.school_id', $school_ids)
->orderBy('school_associate.start_date', 'desc');
return $query;
}

hasOne with null-able in laravel not working

I have a customer table which has a field called 'policy_id', where policy_id points to policy table. It is a null-able field, ie. Some customers may not have a policy.
I have a relationship code like this in Customer.php
public function policy() {
return $this->hasOne('App\Models\Policy', "id", "policy_id");
}
But when I issue a search request I am getting error like this:
Illuminate\Database\Eloquent\ModelNotFoundException: No query results for model [App\Models\Policy]
If I modify the function like this:
public function policy() {
if ($this->getAttribute('policy_id')) {
return $this->hasOne('App\Models\Policy', "id", "policy_id");
} else {
return null
}
}
But I am getting an error like this:
Call to a member function getRelationExistenceQuery() on null
Here is my search code:
$c = new Customer();
return Customer::doesntHave('policy')->orWhere(function (Builder $query) use ($req) {
$query->orWhereHas('policy', function (Builder $query) use ($req) {
$p = new Policy();
$query->where($req->only($p->getFillable()))
->orWhereBetween("policy_period_from", [$req->policy_period_start_from, $req->policy_period_start_to])
->orWhereBetween("policy_period_to", [$req->policy_period_end_from, $req->policy_period_end_to])
->orWhereBetween("payment_date", [$req->payment_date_from, $req->payment_date_to]);
});
})->where($req->only($c->getFillable()))->get();
Am I missing something or are there any other ways to do this?
PS: While debugging the above search code is returning successfully, but the exception happening from somewhere inside Laravel after the prepareResponse call.
Thanks in advance.
return $this->hasOne('App\ModelName', 'foreign_key', 'local_key');
Change the order, put the foreign_key policy_id in front of id
In your Customer Model, you need to use belongsTo method:
public function policy() {
return $this->belongsTo('App\Models\Policy', "policy_id", "id");
}
And In your Policy Model, use hasOne:
public function customer() {
return $this->hasOne('App\Models\Customer', "policy_id", "id");
}
First of all, you placed the wrong params.
$this->belongsTo('App\Models\Policy', "FK", "PK");
public function policy() {
return $this->belongsTo('App\Models\Policy','policy_id', 'id');
}
And for null value of policy_id you can use withDefault();
public function policy() {
return $this->belongsTo('App\Models\Policy','policy_id', 'id')->withDefault([
'name' => 'test'
]);;
}
there's a number of problems there but can you perhaps specify the namespace and the class of both your models - Customer and Policy.
By default, the models you create with php artisan make:model will use the \App namespace e.g. \App\Customer and \App\Policy.
Just double check that.
Also, with regards to the relationship, if the Laravel conventions have been followed you could just:
In the Customer model
public function policy() {
return $this->belongsTo(Policy::class);
}
In the Policy model
public function customer() {
return $this->hasOne(Customer::class);
}
of if a multiple customers can be under one policy
public function customers() {
return $this->hasMany(Customer::class);
}
Good luck

Pass two variable to method in Laravel

i want to find post by slug also in url ..
but the comments must be found by post_id
Controller
public function post($slug,$id)
{
$post = Post::where('slug',$slug)->first();
$comments = Comment::where('post_id',$id)->get();
return view('content.post',compact('post','comments'));
}
Route
Route::get('post/{slug}', 'PagesController#post')->name('post.show');
Route::get('post/{slug}', 'PagesController#post')->name('post.show');
public function post($slug)
{
$post = Post::where('slug',$slug)->first();
$comments = Comment::where('post_id',$post->id)->get();
return view('content.post',compact('post','comments'));
}
Here you go:
Get post_id from the $post itself.
public function post($slug){
$post = Post::where('slug',$slug)->first();
$comments = Comment::where('post_id',$post->id)->get();
...
}
You can use Route Model Binding to ensure that routes will find your model based on the provided key.
Your Post model will require that you add the following method:
public function getRouteKeyName()
{
return 'slug';
}
Then, in your routes, you can just refer the model directly, and binding will happen automatically:
public function post(App\Post $post)
{
$comments = Comment::where('post_id',$post->id)->get();
return view('content.post',compact('post','comments'));
}
This enables you to to use the following route:
Route::get('post/{post}', 'PagesController#post')->name('post.show');
Now, additionally, to ease up your reference of the comments, add them as relations to your Post model:
public function comments()
{
return $this->hasMany(Comment::class);
}
and your Comment model:
public function post()
{
return $this->belongsTo(Post::class);
}
This will allow you to shorten your controller method even more:
public function post(App\Post $post)
{
return view('content.post',compact('post'));
}
and in your Blade view do the following instead:
#foreach($post->comments as $comment)
From: {{ $comment->name }} blah blha
#endforeach
in web.php:
Route::get('post/{slug}', 'PagesController#post')->name('post.show');
in controller:
public function post($slug)
{
$post = Post::where('slug',$slug)->first();
$comments = Comment::where('post_id',$post->id)->get(); //use founded_post_id to find it's comments
return view('content.post',compact('post','comments'));
}

Select records that haven't specific value as a child

I want to get users filtered by category, so I send category_id as a parameter and should select users who haven't records in users_categories_restrictions table with this category_id.
How can I make this eloquent query ??
You could use whereDoesntHave
$users = App\User::whereDoesntHave('categories', function ($query) use($cat_id) {
$query->where('categories.id', '=', $cat_id);
})->get();
I assume you have defined many to many relation between user and category model
class User extends Model
{
public function categories()
{
return $this->belongsToMany(Category::class, 'users_categories_restrictions', 'user_id');
}
}
class Category extends Model
{
public function users()
{
return $this->belongsToMany(User::class, 'users_categories_restrictions', 'category_id');
}
}