Laravel: Retrieving Data in Loop in a One to Many Relationship - laravel

I'm using Laravel 5.8. Here's my code...
CreateSchoolsTable migration
public function up()
{
Schema::create('schools', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->timestamps();
});
}
CreateStudentsTable migration
public function up()
{
Schema::create('students', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('school_id');
$table->string('first_name');
$table->timestamps();
});
}
School model
class School extends Model
{
public function students()
{
return $this->hasMany('App\Student');
}
}
Student Model
class Student extends Model
{
public function school()
{
return $this->belongsTo('App\School');
}
}
Controller
class SchoolsController extends Controller
{
public function index()
{
$schools = School::all();
return view('schools.index', compact('schools'));
}
}
My view: views>schools>index.blade.php
#foreach ($schools as $school)
{{ $school->students->first_name }}
#endforeach
I want to display all the first names in the loop and $school->students->first_name gives me an error.
Property [first_name] does not exist on this collection instance. (View: /Users/philginsburg/Laravel/project1/resources/views/schools/index.blade.php)
When I echo out $school->students it displays an array of the students table, but not sure why I cant loop through with the field name like first_name.

In this case you are dealing with 2 Collections: schools and students (of each school), so you should use 2 different loop to loop through those collections, like this:
#foreach ($schools as $school)
<p>In this school there are those students:</p>
<ul>
#foreach($school->students as $student)
<li>{{ $student->first_name }}</li>
#endforeach
</ul>
#endforeach

The reason is $school->students is array, there is no property called first_name.
you need to
#foreach ($school->students as $student)
{{ $student->first_name }}
#endforeach

Related

Retriving All Polymorphic Relations in Laravel

I've created polymorphic relations in my project. when i want to retrieve them like below
return view('category.index')->with('categories', Category::all()) ;
It returns the relations when i dd() them. However when I want to access the relation in view
when i call it in view it gives me the following result
{"id":1,"image":"abc\/abc.jpg","imageable_id":6,"imageable_type":"App\\Category","created_at":"2020-07-24T13:37:29.000000Z","updated_at":"2020-07-24T13:37:29.000000Z"}
but when i want to access the id or image index it says
Trying to get property 'id' of non-object (View:
D:\WaheedSindhani\Projects\Menu\menu_proj\resources\
views\category\index.blade.php)
i am not picking it up what is happening here
#if(isset($categories))
#foreach($categories as $category)
<tr>
<th scope="row">{{$loop->index+1}}</th>
<td>{{$category->name}}</td>
<td>{{$category->images->image}}</td>
</tr>
#endforeach
#endif
It says no attribute named image found.
Migration are as below
Images Table
public function up()
{
Schema::create('images', function (Blueprint $table) {
$table->id();
$table->string('image');
$table->unsignedInteger('imageable_id');
$table->string('imageable_type');
$table->timestamps();
});
}
Menus Table
public function up()
{
Schema::create('menus', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->boolean('visible');
$table->integer('priority');
$table->timestamps();
});
}
Categories Table
public function up()
{
Schema::create('categories', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->boolean('visibility');
$table->integer('priority');
$table->timestamps();
});
}
Models are as follow
class Menu extends Model
{
public function images()
{
return $this->morphMany( 'App\Image', 'imageable');
}
}
class Image extends Model
{
protected $guarded = [];
public function imageable(){
return $this->morphTo();
}
}
class Category extends Model
{
protected $guarded = [];
public function images()
{
return $this->morphMany(Image::class, 'imageable');
}
}
your problem occurs because the category->images is a Collection of images, not a single image. Because your relation is one to many, not one to one relationship so the soultion would be to loop over the images using a each loop and then do your work there.
that is why you are getting the error that you are trying to access a prop on a non-object.
#if(isset($categories))
#foreach($categories as $category)
<tr>
<th scope="row">{{$loop->index+1}}</th>
<td>{{$category->name}}</td>
#foreach($category->images as $image)
<td>{$image->name}</td>
#endforeach
</tr>
#endforeach
#endif
and this concept is true for every relationship that has a Many in them, hasMany, belongsToMany, etc...

How to return name of table of grade when save like json?

I have two tables, users and grades.
users table
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->string('first_name')->nullable();
$table->string('last_name')->nullable();
$table->string('grade_id')->nullable();
}
}
grades table
public function up()
{
Schema::create('grades', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->timestamps();
});
}
I save id of grade with json_encode,
I save in field like this
$user->grade_id = json_encode($request->input('grade_id'));
I want to return the grade name but I did not solve my problem
Controller
public function index()
{
$grades = Grade::all();
return view('index', compact('grades');
}
User.php
public function grade()
{
return $this->belongsTo(Grade::class);
}
index.blade.php
#if(isset(auth()->user()->grade_id) && !empty(auth()->user()->grade_id))
<?php
$grade = json_decode(auth()->user()->grade->name, true);
#foreach($grade as $key=>$item)
<li>
<div class="grade">{{ $grade[$key] }}</div>
</li>
#endforeach
?>
#endif
I get this errpr
Trying to get property of non-object
Bad answer
You save an array of ids in the users table. Then when you do $grade = json_decode(auth()->user()->grade->name) you get something you don't want to have. Indeed, the value of auth()->user()->grade is ["8", "8"], this is not a grade model. Now you should understand why this $grade = json_decode(auth()->user()->grade->name) fail: you're trying to get name property of a non object (as user()->grade is just an array).
To solve it badly (read the next part of my answer) you can just do this in your controller (you shouldn't do any <?php ?> in your view files):
public function index()
{
$grades_ids = json_decode(auth()->user()->grade); // ["8", "8"]
$grades = Grade::whereIn('id', $grades_ids)->get(); // Eloquent collection
return view('index', compact('grades');
}
And use it in your view:
#if($grades->count())
<ul>
#foreach($grades as $grade)
<li>
<div class="grade">{{ $grade->id }}</div>
</li>
#endforeach
</ul>
#endif
Good answer
Here you have a typical many to many relationships. A grade can have multiple users, and a user can have multiple grades. Then you should think of using a pivot table rather than a encoding several grade_id in json.
Create a users table:
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->string('first_name')->nullable();
$table->string('last_name')->nullable();
}
}
Then you create your grades table:
public function up()
{
Schema::create('grades', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->timestamps();
});
}
Then you create the new user_grade_table:
public function up()
{
Schema::create('user_grade', function (Blueprint $table) {
$table->primary(['user_id', 'grade_id']);
$table->unsignedBigInteger('user_id');
$table->unsignedBigInteger('grade_id');
$table->timestamps();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->foreign('grade_id')->references('id')->on('users')->onDelete('cascade');
});
}
public function down()
{
Schema::table('user_grade', function (Blueprint $table) {
$table->dropPrimary('user_grade_user_id_grade_id_primary');
$table->dropForeign('user_grade_user_id_foreign');
$table->dropForeign('user_grade_grade_id_foreign');
});
Schema::dropIfExists('user_grade');
}
Don't forget to run the migrations. When this is done, you have to update your models, adding the belongs to many on both models.
User model
public function grades()
{
return $this->belongsToMany(Grade::class, 'user_grade', 'user_id', 'grade_id');
}
Same for grade model:
public function users()
{
return $this->belongsToMany(User::class, 'user_grade', 'grade_id', 'user_id');
}
Finally you can use it in your blade file:
#if($grades = auth()->user()->grades)
<ul>
#foreach($grades as $grade)
<li>
<div class="grade">{{ $grade->name }}</div>
</li>
#endforeach
</ul>
#endif

Multi-select dropdown with Many to many relation

In my laravel project I created three tables as employees, teams and employee_teams. There is many to many relationship in "employee_teams" tables, with the foreign keys of employee_id and team_id.
"employee_teams" table DB structure
id | employee_id | team_id
In the Employee form there is a multi-select dropdown which helps to assign multiple teams for the particular employee.
<select name="namedropdown[]" id="namedropdown" class="selectpicker" multiple data-live-search="true">
<option value="" disabled selected>Nothing selected</option>
#foreach ($tdropdown as $key => $tdrop)
<option value="{{$key}}">{{$tdrop}}</option>
#endforeach
</select>
What I want is to save "team_id" and "employee_id", to the "employee_teams" table.
Here is employee model
class employee extends Model
{
public function teams()
{
return $this->belongsToMany(team::class, 'employee_teams');
}
}
Here is team model
class team extends Model
{
public function employees()
{
return $this->belongsToMany(employee::class, 'employee_teams');
}
}
Here is employee_team table migration
class CreateEmployeeTeamsTable extends Migration
{
public function up()
{
Schema::create('employee_teams', function (Blueprint $table) {
$table->increments('id');
$table->integer('employee_id')->unsigned();
$table->integer('team_id')->unsigned();
$table->foreign('employee_id')->references('id')->on('employees')->onDelete('cascade');
$table->foreign('team_id')->references('id')->on('teams')->onDelete('cascade');
$table->timestamps();
});
}
Here is store function in employee controller
use App\employee;
use App\team;
use App\employee_team;
// codes
$employee = new employee();
$employee->namedropdown = implode(',', $request->input('namedropdown')); //Already save multi-select dropdown ids to the database
$employee->save();
$employee_team = new employee_team();
$employee->teams()->attach($employee->namedropdown);
return redirect()->route('employee.index')->with('success','Data Added');
}
Error comes as SQLSTATE[01000]: Warning: 1265 Data truncated for column 'team_id' at row 1
Please help me to save employee_id and team_id to the "employee_teams" table. Thank you so much.
A working sample (tested):
Migration: 2020_06_16_000000_create_employees_table.php
class CreateEmployeesTable extends Migration
{
public function up()
{
Schema::create('employees', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('employees');
}
}
Migration: 2020_06_16_000000_create_teams_table.php
class CreateTeamsTable extends Migration
{
public function up()
{
Schema::create('teams', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('teams');
}
}
Migration: 2020_06_16_000000_create_employee_team_table.php
class CreateEmployeeTeamTable extends Migration
{
public function up()
{
Schema::create('employee_team', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('employee_id')->nullable();
$table->foreign('employee_id')->references('id')->on('employees')->onDelete('cascade');
$table->unsignedBigInteger('team_id')->nullable();
$table->foreign('team_id')->references('id')->on('teams')->onDelete('cascade');
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('employee_team');
}
}
Model: Employee.php
class Employee extends Model
{
public function teams()
{
return $this->belongsToMany('App\Team');
}
}
Model: Team.php
class Team extends Model
{
public function employees()
{
return $this->belongsToMany('App\Employee');
}
}
Routes: routes/web.php:
Route::get('/select_team/{id?}', 'EmployeeController#select_team')->name('employee.select_team');
Route::post('/save_teams/{id?}', 'EmployeeController#save_teams')->name('employee.save_teams');
Controller: EmployeeController.php
use Illuminate\Http\Request;
use App\Team;
use App\Employee;
class EmployeeController extends Controller
{
public function select_team($employee_id){
return view('select_team', ['tdropdown'=>Team::all(), 'employee'=>Employee::find($employee_id)]);
}
public function save_teams(Request $request, $employee_id){
$employee = Employee::find($employee_id);
foreach ($request->namedropdown as $team_id){
$employee->teams()->attach($team_id);
}
return redirect()->route('employee.index')->with('success','Data Added');
}
}
Blade: select_team.blade.php
<!DOCTYPE html>
<form action="/save_teams/{{$employee->id}}" method="post">
#csrf
<select name="namedropdown[]" id="namedropdown" class="selectpicker" multiple data-live-search="true">
<option value="" disabled selected>Nothing selected</option>
#foreach ($tdropdown as $tdrop)
<option value="{{$tdrop->id}}">{{$tdrop->name}}</option>
#endforeach
</select>
<button>Submit</button>
</form>
</html>
I think it's a good example that can give you an idea.

Laravel - must return a relationship instance exception handling

I would like to display employee department based on this relation
here is the JobHistory table schema
Schema::create('JobHistories', function (Blueprint $table) {
$table->increments('id');
$table->integer('employees_id');
$table->integer('Jobs_id');
$table->integer('Departments_id');
$table->date('start_date');
$table->timestamps();
});
here is the employee model
public function jobs(){
return $this->hasMany(JobHistory::class,'employees_id','id');
}
public function getDepartment()
{
$dep = JobHistory::where('employees_id', $this->id)->orderBy('start_date', 'desc')->first();
return ($dep) ? $dep->department() : '';
}
here is function on JobHistory Model
public function department()
{
return $this->belongsTo(Department::class, 'Departments_id', 'id');
}
And finally I want to display it on view like this:
#foreach($employee as $row)
<tr>
<td>{{$row->name}}</td>
<td>
{{$row->getDepartment->name or '-'}}
</td>
</tr>
#endforeach
The question is, how to handle exception if an employee does not have any job history yet? I have try using is_null(), empty() and isset() on blade validation. but nothing works.
You can Use ternary operator
Here is solution to your problem
{{isset($row->getDepartment->name) ? $row->getDepartment->name : '-'}}
Hope it will work for you

List out subcategories under parentcategory based on product

As mentioned in the title, the problem is how to list the subcategories under the parent category based on the product. I have no problem listing out the parent category and all its subcategories as I have shown in the sample code at the end of this post. But I need to list out the subcategories based on the product.
This is the sample structure of my categories:
Electronic
-Computer
-Phone
-Gadget
Grocery
-Food
-Drinks
This is my products table migration:
Schema::create('products', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->decimal('price')->nullable();
$table->timestamps();
});
This is my categories table migration
Schema::create('categories', function (Blueprint $table) {
$table->increments('id');
$table->integer('parent_id')->nullable();
$table->string('name')->nullable();
$table->string('description')->nullable();
$table->timestamps();
});
and this one is the category_product which acts as for many to many table between category and product:
Schema::create('category_product', function (Blueprint $table) {
$table->increments('id');
$table->integer('product_id')->unsigned()->index();
$table->foreign('product_id')->references('id')->on('products')->onDelete('cascade');
$table->integer('category_id')->unsigned()->index();
$table->foreign('category_id')->references('id')->on('categories')->onDelete('cascade');
$table->timestamps();
});
I have setup all the relationship. Here are my models:
This is my category model:
class Category extends Model
{
protected $table = 'categories';
protected $fillable = [
'name',
];
public function products()
{
return $this->belongsToMany('App\Product');
}
public function parent()
{
return $this->belongsTo('App\Category', 'parent_id');
}
public function children()
{
return $this->hasMany('App\Category', 'parent_id');
}
}
This is my product model:
class Product extends Model
{
public function categories()
{
return $this->belongsToMany('App\Category');
}
}
This is my ProductController.php, I'm able to display all parents category with its subcategories using this code:
public function show($id)
{
$product = Product::findOrFail($id);
$categories = Category::with('children')->get();
return view('products.show', compact('product','categories'));
}
So my product.shows.blade looks like this:
#foreach($categories as $item)
#if($item->children->count() > 0 )
<li>
{{ $item->name }}
<ul>
#foreach($item->children as $submenu)
<li>{{ $submenu->name }}</li>
#endforeach
</ul>
</li>
#endif
#endforeach
//OUTPUT:
Electronic
Computer
Phone
Gadget
Grocery
Food
Drinks
But let say that this particular product (called Product 1) has the parent category of Electronic and the subcategory is Computer and Phone which I already attached them in the database. This is the overview of the data from the database:
How do I display the category of Product 1 with its parent and subcategories? I want the output to be like
Product 1
Category:
Electronic
Computer
Phone
Update:
So, another thing that I have done was adding the $product->categories but the code will only list out a parent category that the Product1 has with all its subcategories. It doesn't filter the subcategories specific to the Product1
#foreach($product->categories as $item)
#if($item->children->count() > 0 )
<li>
{{ $item->name }}
<ul>
#foreach($item->children as $submenu)
<li>{{ $submenu->name }}</li>
#endforeach
</ul>
</li>
#endif
#endforeach
So, instead of the output like this (which i want):
Category:
Electronic
Computer
Phone
it will list out like this (which is not i want):
Category:
Electronic
Computer
Phone
Gadget
Roughly like so:
Controller
$product = Product::with('categories.parent')->findOrFail($id);
$categories = $product->categories->groupBy('parent_id');
#foreach($categories as $parent_id => $children)
#if($children->first()->parent}}
Parent {{$children->first()->parent->name}}
#foreach($children as $child)
//print child
#endforeach
#endif
#endforeach

Resources