One to Many relationship in Laravel Eloquent giving undefine method - laravel

I am having the said problem when defining one to many relationship with two models, the Student and Enrollment. When accessing the table from another table using:
$enrollment = App\Enrollment::first();
$enrollment->students()->first_name;
Im getting :
Illuminate\Database\QueryException with message 'SQLSTATE[42S22]: Column not found: 1054 Unknown column 'students.enrollment_id' in 'where clause' (SQL: select * from `students` where `students`.`enrollment_id` = 1 and `students`.`enrollment_id` is not null)'
However when I use :
$enrollment = App\Enrollment::first();
$enrollment->students()->first_name;
Im getting :
PHP error: Undefined property: Illuminate\Database\Eloquent\Relations\HasMany::$first_name on line 1
Can someone help me on this?
Enrollment
protected $fillable = [
'subject_code',
'subject_description',
'section',
'schedule',
'room_no',
'no_of_units'
];
public function students()
{
return $this->hasMany('App\Student');
}
Student
protected $fillable = [
'enrollments_id',
'student_no',
'first_name',
'last_name',
'middle_name',
'birthdate',
'fathers_name',
'mothers_name',
'phone_no',
'degree_id',
'city_id',
'address'
];
public function enrollment()
{
return $this->belongsTo('App\Enrollment');
}
Here's the table for the students and enrollment accordingly
Schema::create('students', function (Blueprint $table) {
$table->increments('id')->unsigned();
$table->integer('enrollments_id')->unsigned();
$table->integer('student_no');
$table->foreign('enrollments_id')->references('id')->on('enrollments')->unsigned();
$table->string('first_name');
$table->string('last_name');
$table->string('middle_name');
$table->date('birthdate');
$table->string('fathers_name');
$table->string('mothers_name');
$table->string('phone_no');
$table->string('address');
$table->integer('city_id');
$table->integer('degree_id');
$table->timestamps();
});
Schema::create('enrollments', function (Blueprint $table) {
$table->increments('id');
$table->string('subject_description');
$table->string('subject_code');
$table->time('schedule');
$table->string('room_no');
$table->integer('no_of_units');
$table->string('section');
$table->timestamps();
});

First of all what you exactly want
as per your question i am assuming you want 'enrollment has many students', for this relationship you need enrolment_id in students table not student_id in enrollment table.
and after that use
$students= Enrolment::find(id)->students;
it will return all students with the required enrollment id

That normal since you're trying to get property first_name from hasMany object, you should use get() method if you want to get Collection of objects (in your case collection of students) :
$enrollment->students()->get();
And if you want to get the first name you should specify the object you want to get the first_name from it, e.g :
$enrollment->students()->first()->first_name;
$enrollment->students()->last()->first_name;
If you want to get the attribute first_name of all the students you should use lists method, e.g :
$enrollment->students()->lists('first_name');
Output: collection of all students first names.
Hope this helps.
Update :
Try to remove ->unsigned() in :
$table->increments('id')->unsigned();
and also in :
$table->foreign('enrollments_id')->references('id')->on('enrollments')->unsigned();

When you call relations on an eloquent object it actually return you the relation not the exact objects
for eg: in your case
$enrollment->students() will return the relation
where as
$enrollment->students will return the eloquent object ( in belongs to, has one relations ) and collection/array of eloquent object/objects(in one to many , many to many relations )
simply if you are using it as an attribute you will get the actual result and relation query if it is called as a function
even if you get the relation you can get the results by calling get on top of that
$enrollment->students()->get()

Related

How with pivot relation set additive filter?

In laravel 9 app I use many to many relation with table article_vote joining 2 tables :
return new class extends Migration {
public function up()
{
Schema::create('article_vote', function (Blueprint $table) {
$table->id();
$table->foreignId('article_id')->references('id')->on('articles')->onUpdate('RESTRICT')->onDelete('CASCADE');
$table->foreignId('vote_id')->references('id')->on('votes')->onUpdate('RESTRICT')->onDelete('CASCADE');
...
});
}
In app/Models/Article.php model I have relation :
public function onlyActiveVotes(): BelongsToMany
{
return $this->belongsToMany(Vote::class, 'article_vote', 'article_id')
->withTimestamps()
->wherePivot('active', true)
->withPivot(['active', 'expired_at', 'supervisor_id', 'supervisor_notes']);
}
and I want having Article by Id($id)
$article = Article::getById($id)
->firstOrFail();
Using onlyActiveVotes relation to get filtered data from vote :
$voteTableName = ((new Vote)->getTable());
$articleVotes = $article->onlyActiveVotes()::whereHas('votes', function ($query) use($request, $voteTableName) {
$query->where($voteTableName . '.vote_category_id', $request->filter_vote_vote_category_id);
})->get();
It does not work, as I got error :
Method Illuminate\Database\Eloquent\Relations\BelongsToMany::whereHas does not exist. i
Line below returns collection
dd($article->onlyActiveVotes );
Line below returns BelongsToMany:
dd($article->onlyActiveVotes() );
Illuminate\Database\Eloquent\Relations\BelongsToMany {#2283 // app/Repositories/ArticleToManyVotesRepository.php:74
#query: Illuminate\Database\Eloquent\Builder {#2218
#query: Illuminate\Database\Query\Builder {#2268
On
dd($article->onlyActiveVotes()->query );
I got error:
Cannot access protected property Illuminate\Database\Eloquent\Relations\BelongsToMany::$query
If there is a way to use whereHas with onlyActiveVotes relation ?
Updated BLOCK :
I hope I clearly exoplaned what I want : want to get only filtered votes which arerelated with pivot through $article model
Aftet I fixed :
$filteredArticleVotes = $article->onlyActiveVotes()->whereHas
I got other error :
Call to undefined method App\Models\Vote::onlyActiveVotes()
pointing at line
$filteredArticleVotes = $article->onlyActiveVotes()->whereHas('onlyActiveVotes', function ($query) use($request, $voteTableName) {
$query->where($voteTableName . '.vote_category_id', $request->filter_vote_vote_category_id);
})->get();
As I wrote in my post Article model has onlyActiveVotes method and I expected the code above have to work, but it did not...
Thanks!
Since onlyActiveVotes() already returns a query builder for the votes table, you can directly chain the where() method to filter the results by the vote_category_id column. The whereHas() method is not necessary in this case.
$articleVotes = $article->onlyActiveVotes()
->where('vote_category_id', $request->filter_vote_vote_category_id)
->get();

Retrieving eloquent models with constraints on both the parent and child/associated model

I'm trying to retrieve all Eloquent Models that match a particular field in the Parent Model ('Event') and the child model ('Dates').
I've hit an issue whereby Laravel is stating that my field ('date') doesn't exist in the child class, but I can't understand why. Can someone please point me in the right direction and explain where I've gone wrong?
Essentially, what I'm trying to achieve is the retrieval of all Events with the approved tag being true AND where the event date is of a particular day, in this case the 10th.
I've done some searching around and looked at some of the examples in the Laravel documentation. I've set up the ('Event') model to have a one to many relationship with the ('dates') model. I can see that I can chain queries together, but things get a little confusing when dealing with more than one model at a time (in the same query)
This is my attempt at retrieving the data.
public function calender()
{
$events = Event::where('approved', true)->with('EventDates')->whereDay('date', '10')->get();
return view('events.calender');
}
This is a snippet from my ('Event') Model. I've only included the most relevant information here as there are many attributes .
class Event extends Model
{
//
public function user(){
return $this->belongsTo(User::class);
}
public function dates()
{
return $this->hasMany('App\EventDate');
}
}
This is a snippet from my ('EventDate') model migration file showing that 'date' is indeed a field of the ('EventDate') model. Once again, I've just included the most relevant function here.
class CreateEventDatesTable extends Migration
{
public function up()
{
Schema::create('event_dates', function (Blueprint $table) {
$table->bigIncrements('id');
$table->timestamps();
$table->date('date')->nullable();
$table->time('startTime')->nullable();
$table->time('endTime')->nullable();
$table->unsignedBigInteger('event_id');
$table->index('event_id');
});
}
}
I'd like to be able to retrieve a list of the matching Events that have the approved attribute set to true, and the Event Dates on a particular day (xxxx-xx-10)
Right now, I'm getting the error that the date column can't be found:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'date' in 'where clause' (SQL: select * from events where day(date) = 10 and approved = 1)
I think this is what you are looking for:
$events = Event::where('approved', true)
->with(['dates' => function ($query) {
return $query->whereDay('date', '10');
}])
->get();
Note: I assume your relation between an Event and it's EventDate is called dates
In this way you are applying filtering by day on the related data (EventDate) and not on the Event model.
you should mention the table name for the date column.
->whereDay('event_dates.date', '10')
I managed to find a way around using the eloquent commands by using the DB Query builder instead and it now works (taking into account Shankar's point above)
I altered the code to this (It's a little more specific than what I wrote before, but it should be able to guide others to solving their issue):
for($day = 5; $day <12; $day++)
{
$events = DB::table('events')
->join('event_dates', 'events.id', '=', 'event_dates.event_id')
->select('events.*', 'event_dates.startTime as startTime', 'event_dates.endTime AS endTime')->where('events.approved', '=', true)->whereDay('event_dates.date', '=', $day)
->orderBy('event_dates.startTime', 'asc')->get();
array_push($events_list, $events);
}
```
I can't understand why the Eloquent queries couldn't find the table, but this seems to work, so for those stuck, this may be less "eloquent" but at least it works ;-;

Is there any possible way to generate database from models in Laravel?

Is there any possible way to generate database from models in Laravel ?
I am working on an existing laravel project,it contains models without their migrations.
I tried the reliese/laravel also the doctrine but nothing won't work ..any help ?
Suppose you have model Userwithout migration then you can get table name from it like
User::getTableName(); there is same function to get column names and you can get column types from protected $cast = []
Here is Sample code
$location = new LocationTable(); // load your model
$tableName = $location->getTable(); // this will give a table name from your model
$columnsWithType = $location->getCasts(); // set all your table fields as cast
Schema::create(tableName , function (Blueprint $table) use ($columnsWithType) {
foreach ($columnsWithType as $columnName => $type) {
$table->$type($columnName);
}
});

Eloquent using wrong key for some tables

Using: Laravel 5.5
I Constructing addresses using some address elements (like: district, area, zip etc) as dropdowns & also some user inputs.
I have 5 address element & one of their schema is:
Schema::create('address_districts', function (Blueprint $table) {
$table->increments('id');
$table->integer('admin_id')->unsigned();
$table->string('name');
$table->timestamps();
$table->foreign('admin_id')->references('id')->on('admins');
});
This is for Districts, & I have another 3 exactly same like this called, address_thanas, address_areas, address_building_names & address_zips;
The only exception for the last one is that has code instead of name on other tables:
Schema::create('address_zips', function (Blueprint $table) {
$table->increments('id');
$table->integer('admin_id')->unsigned();
$table->string('code'); // Look other table has name here........
$table->timestamps();
$table->foreign('admin_id')->references('id')->on('admins');
});
I store constructed addresses on the table called addresses
Schema::create('addresses', function (Blueprint $table) {
$table->increments('id');
$table->integer('district_id')->unsigned();
$table->integer('thana_id')->unsigned();
$table->integer('area_id')->unsigned();
$table->integer('zip_id')->unsigned();
$table->integer('building_name_id')->nullable()->unsigned();
$table->string('building');
$table->integer('floor');
$table->string('apt')->nullable();
$table->text('comment')->nullable();
$table->timestamps();
$table->foreign('district_id')->references('id')->on('address_districts');
$table->foreign('thana_id')->references('id')->on('address_thanas');
$table->foreign('area_id')->references('id')->on('address_areas');
$table->foreign('zip_id')->references('id')->on('address_zips');
$table->foreign('building_name_id')->references('id')->on('address_building_names');
});
In Address Model I've defined relationships like:
public function district() {
return $this->belongsTo(AddressDistrict::class, 'district_id');
}
public function thana() {
return $this->belongsTo(AddressThana::class, 'thana_id');
}
public function area() {
return $this->belongsTo(AddressArea::class, 'area_id');
}
public function building_name() {
return $this->belongsTo(AddressBuildingName::class, 'building_name_id');
}
public function zip() {
return $this->belongsTo(AddressZip::class, 'zip_id', 'id');
}
Then when I try to create a new address using Address::create($data)
I get error:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'name' in 'where clause' (SQL: select count(*) as aggregate from `address_zips` where `name` = 2)
Here we can see that it is comparing the key name instead of id
I noticed that this is not reporting that the data cannot be inserted or something like that, it fails to count related model & for that aborts insertion of data
Why is that?
But the strange thing is I can retrieve data by (inserted a row manually into db for testing to see if that can retrieve data)
$addresses = Address::orderByDesc('created_at')->get();
//loop as $address
$address->district->name
$address->zip->code
...
& this works perfect
When I am creating a new record I need that query look like:
select count(*) as aggregate from `address_zips` where `id` = 2
Any help will be highly appreciated.
Thanks for reading this long question.
The problem is in your $data array. Probably you copied a form with an input name, you need to rename it to code in order to save the values automatically.
Alternatively, you can set the input manually
$address = new Address();
$address->code = request('name');
$address->save();

Laravel Manual Update Timestamp

I have a miscellaneous database table that is updated each time a user visits another user's profile. I'm having trouble creating a manual timestamp column though. Since I didn't create a model for this, I'm doing it manually which shouldn't be an issue because this database isn't accepting any user-generated input.
Migration:
public function up()
{
Schema::create('recently_visited', function (Blueprint $table) {
$table->increments('id');
$table->integer('visitor_id');
$table->integer('profile_id');
$table->integer('times_visited');
$table->timestamp('last_visit');
});
}
Controller:
$visits = DB::table('recently_visited')
->where('profile_id',$user->id)
->where('visitor_id',Auth::user()->id)
->increment('times_visited')
->update(array('last_visit' => Carbon::now()));
I get the following error. Please help me understand, what is the integer this is being called on. Thanks!
Fatal error: Call to a member function update() on integer
ANSWER:
I just needed to include all my updated within the increment() in the query builder.
$visits = DB::table('recently_visited')
->where('profile_id',$user->id)
->where('visitor_id',Auth::user()->id)
->increment('times_visited', 1, ['last_visit' => Carbon::now()]);
Please help me understand, what is the integer this is being called on.
increment() method doesn't return query builder instance but rather updates the record itself and returns an integer.
You should be able to do this:
$visits = DB::table('recently_visited')
->where('profile_id',$user->id)
->where('visitor_id',Auth::user()->id)
->increment('times_visited', 1, array('last_visit' => Carbon::now()));

Resources