I'm using Eloquent in Laravel 5 and am a bit confused in relations between tables. These are existing tables of a software and cannot be altered.
I have the following tables that shows historical details of a student. All tables have model classes created.
student
-------
student_id (PK)
first_name
last_name
class
-----
class_id (PK)
name
section
year //2012, 2013 etc
student_id (FK)
result
------
result_id (PK)
class_id (FK)
month // jan, feb etc
status //pass or fail
Below are the details of each table
Student studies in a class for a particular year
Each class for that year will have 10 results (1 result for each month in an year)
Each result will contain the state for a test in a month (pass or fail)
I want to access objects based on the below examples. Can you guide on setting up relations in the model classes
student->class(2013)->result(jan)
result(feb)->class(2016)->student
First of all you should define the relationships between your models:
Result.php model
public function class(){
return $this->belongsTo('Class::class');
}
Class.php model
public function results(){
return $this->hasMany('Result::class');
}
public function student(){
return $this->belongsTo('Student::class');
}
Student.php model
public function classes(){
return $this->hasMany('Class::class');
}
Then, you can use some queries. For your first example student->class(2013)->result(jan):
$results = Student::find($student_id)->classes()->where('year', '2013')->results()->where('month', 'jan')->get();
Also, if you will need to search classes by year and results by month a lot of times, you can easily add some methods like this in your models:
Student.php model
public function classesByYear($year){
return $this->classes()->where('year', $year)->get();
}
Class.php model
public function resultsByMonth($month){
return $this->results()->where('month', $month)->get();
}
So the previous example will become:
$results = Student::find($student_id)->classesByYear('2013')->resultsByMonth('jan');
EDIT: Your tables are using a custom identifier, so you must implement protected $primaryKey = 'custom_id'; on each model.
Related
I have 3 tables:
Events
Ages
Books
Which I was relating them in the following way:
ages
id
name
books
id
title
age_book
id
age_id
book_id
events
id
name
event_age
id
event_id
age_id
event_age_book
event_age_id
book_id
Basically, a book can have many ages, an age can have many books, an event can have many ages, an event age can have many books.
My Event Model looks like this and works fine to get all event ages
class Event extends Model
{
public function ages()
{
return $this->belongsToMany(Age::class, 'event_age');
}
}
Age Model:
class Age extends Model
{
public function books(): BelongsToMany
{
return $this
->belongsToMany(Book::class)
->withTimestamps();
}
}
Book Model
class Book extends Model
{
public function ages(): BelongsToMany
{
return $this
->belongsToMany(Age::class, 'book_age', 'book_id','age_id')
->withTimestamps();
}
}
Im having trouble figuring out how to get all edition age books, is it possible to do it on the Event model?
Let's Review The Relationships
Seems you should only need 3 models, and 5 tables.
Books Belong to Ages
Events Belong to Ages
Ages Belongs to Books
Ages Belongs to Events
Your other models are alright, but your age model is missing a relationship to Event:
class Age extends Model
{
public function books(): BelongsToMany
{
return $this->belongsToMany(Book::class)
->withTimestamps();
}
public function events(): BelongsToMany
{
return $this->belongsToMany(Events::class,'event_age')
->withTimestamps();
}
}
That would allow:
$books->ages;
$events->ages;
$ages->book;
$ages->events;
And chaining...
$books = collect();
foreach($event->ages as $age){
$books->merge($ages->books);
}
$books->unique();
Books and Events
So, I gon't think you want age_event_books. I think what you really want is:
Events belong to Books
Books belong to Events
such that
book_events
- id
- book_id
- event_id
- age_id
And you'd have in book:
public function events()
{
return $this->BelongsToMany('App\Event')
->withTimestamps()
->withPivot('age_id');
}
And in event:
public function books()
{
return $this->belongsToMany('App\Book')->withTimestamps()
->withPiviot('age_id')->groupBy('age_id');
}
Giving you:
$event->books
$book->events
On the Front End
[O]n the frontend i'll need to get the most recent event and group books by age and books can belong to more than one age
$event = Event::latest();
$books = $event->books();
Then on the blade
#foreach($books as $age_id => $books)
<h4>{{Age::find($age_id)->name}}</h4>
#foreach($books as $book)
<div>$book->name</div>
#endforeach
#endforeach
Helpful Tip
You are supplying the relation table, which you have to do because you didn't follow the naming convention for joining tables. The convention is that the classes being joined should be listed alphabetically. So age_event instead of event_age.
I work with laravel 5.4, and I want receive information from my relations tables.
I have 3 tables in phpmyadmin
ATHLETES
ID
FIRST_NAME
LAST_NAME
TYPES
ID
NAME
SLUG
ATHLETES_TYPES
ID
TYPE_ID
ATHLETE_ID
I have 3 models
ATHLETE
TYPE
ATHLETEBYTYPE
How do I need to make the relations models to have name and slug from my table TYPES, with my id from my table athletes?
Thank you.
For this you just need 2 model file ATHLETE and TYPE and use many to many relation.and then you can use ATHLETES_TYPES table as pivot table.
for implement this :
first add this method to ATHLETE model file :
public function types()
{
return $this->belongsToMany(TYPE::class,'ATHLETES_TYPES','ATHLETE_ID','TYPE_ID');
}
secode add this method to TYPE model :
public function athletes()
{
return $this->belongsToMany(ATHLETE::class,'ATHLETES_TYPES','TYPE_ID','ATHLETE_ID');
}
and in last remove ID filed from ATHLETES_TYPES table.
done.
now if you have variable from ATHLETE type with $ATHLETE->types you can get types of than.
read more here :
https://laravel.com/docs/5.4/eloquent-relationships#many-to-many
Here are the relationships, buddy
In App/Athlete.php
class Athlete extends Model
{
public function types(){
return $this->belongsToMany('App\Type');
}
}
In App/Type.php
class Type extends Model
{
public function Athletes(){
return $this->belongsToMany('App\Athlete');
}
}
I have three entities:
User
Profile
Status
Summarized, users has a profile foreach status.
EXAMPLE
Users table records
user
id | name
1 user1
2 user2
3 user3
Status table records (this records is previously seeded)
status
id (PK) | title (string)
1 student
2 collaborator
3 teacher
Profile table structure
PROFILE
user_status_id (PK) (FK)
institution (string)
year (timestamp)
user1 is registered as student and fill your profile based on this status. After, he change your status to collaborator and fill another profile referred to this new status.
With that, i want to know:
When user1 was student?
the user1 was already collaborator ever?
Which institution user1 had frequented when he was student?
My first problem is:
How to model this relationship?
I'm not sure but think the above situation is a ternary relatinoship case, right?
And Second problem:
How build using Laravel Eloquent Relationships?
Let's start with relationship
class User extends Eloquent{
protected $table = 'user';
public $timestamps = true;
use SoftDeletingTrait;
public function status(){
return $this->hasOne(Status::class, 'user_id', 'id');
}
public function profile(){
return $this->hasMany(Profile::class,'user_id','id');
}
}
class Status extends Eloquent{
protected $table = 'status';
public $timestamps = true;
use SoftDeletingTrait;
public function user(){
return $this->belongsTo(User::class,'user_id','id');
}
}
class Profile extends Eloquent{
protected $table = 'profile';
public $timestamps = true;
use SoftDeletingTrait;
public function user(){
return $this->belongsTo(User::class,'user_id','id');
}
}
Above relation would somehow will get you what you need but just writing this relation made me question your table structure.
Suggestion DB Change For Better Relation
User
id, username, email, ........
Status
id, status, description, ........
Institutions
id, name, address, ............
User_Status_Institution
user_id,status_id,institution_id,timestamp,.......
This structure allows us that we can have Institution details even if there is no user_profile for that institution. Previous structure you had you can only have institution (name only) detail if and only if there is user profile. Also you can change institution (Name/address/phone etc) at one place and it will update it for all users.
As much as possible try that you can make information to be independent if possible because that reflects in models, object and relations.
Hope it makes sense.
I was trying to get the attendance of each user in a course where the users and the courses have many to many relationship and the attendance table is based on the relationship.
But i couldn't call $user->pivot->attendance(); directly i had to call for the pivot object it self so i can call a function to it as you can see in the answer
The Following is a sample of my Database scheme
I need to run
$users=$course->users;
foreach($users as $user)
{
$user->pivot->attendance(); //error in this line
}
this line gives and error
Call to undefined method
Illuminate\Database\Query\Builder::attendance()
users
id
name
etc
courses
id
name
startDate
endDate
course_user
id
course_id
user_id
payment
regDate
status
userattendance
id
course_user_id
time
inOrOut
And here is the CourseUserPivot Class
class CourseUserPivot extends Eloquent {
protected $table ="course_user";
public function course()
{
return $this->belongsTo('Course');
}
public function user()
{
return $this->belongsTo('User');
}
public function attendance()
{
return $this->hasMany('Userattendance','course_user_id');
}
}
P.S: $user->pivot->payment works and displays the attribute but i cant call methods
You can't use $user->pivot->attendance(); as this calls attendance on the user object not on the pivot object
You will have to fetch the pivot object then call the function like so
CourseUserPivot::find($user->pivot->id)->attendance();
make user that where you used withPivot() function you include the id in the array
like in
class Course{
...
...
public function users()
{
return $this->belongsToMany('Candidate')
->withPivot('id',
'summary',
'status',
'payment'
);
}
}
I was trying to accomplish the same thing, and I was having trouble getting it to work like I wanted until I included the 'id' column of my custom pivot model in the withPivot() method.
For some context, my database structure is:
User table
id
Items Table
id
Sets table (defines a set and the items that can be in an instance of it)
id
Set_User table (Custom Pivot Model, instances of a set created by a user)
id, set_id, user_id
Items_SetUser table (items that belong to a set instance that belong to a user)
item_id, setuser_id
I do this in the controller to get all the sets that user has made an instance of, with custom pivot table data:
$user_sets = \Auth::user()->sets()->withPivot('id', 'name')->get();
And then in blade I can access the items associated with each set instance like this:
$set->pivot->set_items()->count()
Without including 'id' in the call to withPivot(), the above is not possible. Note that set_items() is a method on the custom pivot model that defines a relationship with the items table.
I have two tables 1)users
{ id, password }
2)expertise { id, expertise}
the relationship I have is
Models
Expertise.php
function User()
{
$this->hasOne('Expertise');
}
User.php
function Expertise()
{
$this->hasOne('User');
}
So how can I query using Eloquent to get the first 10 users with a certain expertise?
I want to join users.id = expertise.id and get the first 10 people with a specified expertise (Where clause).
Beginner to laravel, I've checked other sources but was not successful
Right now you are having a problem with the way that you modeled your data. If you have a one-to-one relationship the best practice to model it is to have one entity store the id of the other. The Laravel convention for this is to have a column named <model>_id:
Users
| id | password |
Expertises
| id | expertise | user_id |
Then in your models you can do this:
Models
Expertise.php
class Expertise extends Eloquent
{
public function User()
{
// because expertise has a column user_id
// expertise belongs to user
return $this->belongsTo('User');
}
}
User.php
class User extends Eloquent
{
public function Expertise()
{
// because expertise is the one with the column
// user_id, user has one expertise
return $this->hasOne('Expertise');
}
}
The Query
After you have all this set up, to be able to query the first 10 users with a certain expertise you can do this.
$users = User::whereHas('Expertise', function($q)
{
$q->where('expertise', '=', <expertise you are looking for>)
})
->take(10)
->get();
To get a further reading in querying relationships in Laravel please take a look at this:
Laravel - Querying Relationships
Keep in mind
keep in mind that the tables name must be plural, if not then you should specify the name of the table inside the model:
protected $table = 'expertise';