Laravel Eloquent - orderBy column find in related model - eloquent

INFORMATIONS
Database
events | id | to_user_id | from_user_id |
event_details | id | event_id | when |
Event.php
class Event extends Eloquent {
protected $table = 'events';
public function event_detail() {
return $this->hasOne('EventDetail');
}}
EventDetail.php
class EventDetail extends Eloquent {
protected $table = 'event_details';
public function event() {
return $this->belongsTo('Event', 'event_id');
}}
QUESTION
I want to get all events (Event model) and order by 'when' column in related EventDetail model. I wrote a query and it works:
$event = Event::join('event_details as p', 'p.event_id', '=', 'event.id')
->orderBy('p.when', 'asc')
->select('events.*')
->with('event_detail')
->get();
but when i would like to add ->where('to_user_id', '=', 0) clause i get error. How can I fix it?
Moreover I want to know if this query is correct with good practise?
Can you write it better?

Ok, had to recreate your project/tables
All tests are running correctly but I have noticed a few things you may consider changing.
$events = Event::with(['event_detail'=> function($query) {
$query->orderBy('when', 'asc');
}])
->where('to_user_id', 0)
->get();
Also your code may be better if you have an Event
public function event_detail(){
//considering you will have multiples? or just the one event detail?
return $this->hasMany('EventDetail');
}
As you can see, the one event I stored has 2 event details & they are all ordered by ASC.
You can use the other method that doesnt utilize 'with()' but I prefer it. Very nice for json results where you can daisychain a lot of related models.
Hope this helps

I solved the problem with this query. I should add ->where('events.to_user_id', '=', 0) so
$event = Event::join('event_details as p', 'p.event_id', '=', 'event.id')
->orderBy('p.when', 'asc')
->select('events.*')
->where('events.to_user_id', '=', 0)
->with('event_detail')
->get();
However, I would like to know if this query is correct with good practise? Can you write it better?

Related

Balancing between filter and getting the name of foreign key in eloquent

I have a controller that will list all items like this:
$users = User::all();
return view('auth.userslist', compact('users'));
And In my Users Model, function that will display the name of foreign key items:
public function role() {
return $this->belongsTo('App\Models\Roles');
}
Displayed in blade like this:
<td>{{$user->name}}</td>
<td>{{$user->role->role_name}}</td>
Which works fine. Now, I want to add a filter for my user list(Active and Inactive). Based on docs, I need to do it like this:
$users = DB::table('users')
->where('user_delete', '=', 0)
->get();
return view('auth.userslist', ['users' => $users]);
But it will return an error message Undefined property: stdClass::$role. If I remove all the foreign key fields, it will filter just fine.
Use with() or join
$users = User::with('role')->where('user_delete', '=', 0)
->get();

Laravel Eloquent Many-To-Many Query Producing Extra null WHERE clause

I'm using Eloquent to produce results from a query comprised of three tables:
photos (id)
photos_to_photosets (photo_id, photoset_id)
photosets (id)
My models have their many-to-many relationships defined as:
class Photo extends Model
{
public function photosets()
{
return $this->hasMany(PhotoSet::class, 'photos_to_photo_sets');
}
}
And
class PhotoSets extends Model
{
public function photos()
{
return $this->belongsToMany(Photo::class, 'photos_to_photo_sets');
}
}
Now, to fetch results I'm forced to use the following Eloquent code:
$photoData = $this->photoSets->photos()
->orWhere('photo_set_id', '=', $id)
->get();
This produces the following query:
SELECT * FROM `photos`
INNER JOIN `photos_to_photo_sets`
ON `photos`.`id` = `photos_to_photo_sets`.`photo_id`
WHERE `photos_to_photo_sets`.`photo_set_id` is null
OR `photo_set_id` = ?
This query works, but I can't seem to remove WHERE `photos_to_photo_sets`.`photo_set_id` is null from the query.
I've tried to just use ->where('photo_set_id', '=', $id) but the null clause still remains; even worse it produces the following WHERE clause:
... WHERE `photos_to_photo_sets`.`photo_set_id` IS NULL
AND `photo_set_id` = ?
Is there any way, utilizing Eloquent, to remove this null WHERE clause segment?
Ideally, I'd like to end up with the following query:
SELECT * FROM `photos`
INNER JOIN `photos_to_photo_sets`
ON `photos`.`id` = `photos_to_photo_sets`.`photo_id`
WHERE `photo_set_id` = ?
Thank you in advance for any help!
UPDATE
Based off #Jonas Staudenmeir's answer, the null WHERE clause has been removed.
To achieve this, I set the photoSet's model ID prior to running the query. The resulting code was:
$this->photoSets->id = $photoset_id;
$photoData = $this->photoSets->photos()->get();
Which produced:
SELECT * FROM `photos`
INNER JOIN `photos_to_photo_sets`
ON `photos`.`id` = `photos_to_photo_sets`.`photo_id`
WHERE `photo_set_id` = ?
The injected model doesn't have an id:
$this->photoSet->id = $id;
Then you don't need the additional constraint:
$photoData = $this->photoSets->photos;

Yii2: How to do a simple join query?

I am learning how to do simple queries using the Yii2 framework. I use PostgreSQL.
I am trying to join two tables and get the data from both tables with a where condition.
The tables are called Admins and Persons.
The join use field called idadm.
The condition is idadm = 33. This works great but the result has data only from the Admins table and I need data from the other table.
Here is my example:
$query = \app\models\Admins::find()
->select('*')
->leftJoin('persons', 'persons.idadm = admins.idadm')
->where(['admins.idadm' => 33])
->with('persons')
->all();
I am following the Yii2 official guide: http://www.yiiframework.com/doc-2.0/guide-db-active-record.html
Update: Here I show the updated code that doesn't solve de problem:
You need to write all column name in select().
$query = \app\models\Admins::find()
->select('admin.*,persons.*') // make sure same column name not there in both table
->leftJoin('persons', 'persons.idadm = admins.idadm')
->where(['admins.idadm' => 33])
->with('persons')
->all();
And also you need to define person table attributes in Admin model.
Second way is get records as array,so you dont need to define attributes in Admin model.
$query = \app\models\Admins::find()
->select('admin.*,persons.*') // make sure same column name not there in both table
->leftJoin('persons', 'persons.idadm = admins.idadm')
->where(['admins.idadm' => 33])
->with('persons')
->asArray()
->all();
Ensure that active record has required relations, e.g. something like follows:
class Admins extends \yii\db\ActiveRecord {
public function table() {
return "admins";
}
public function getPersons()
{
return $this->hasMany(Person::className(), ['idadm' => 'idadm']);
}
}
class Person extends \yii\db\ActiveRecord {
public function table() {
return "persons";
}
}
Then use joinWith to build query:
$query = Admins::find()
->joinWith('persons')
->limit(1);
$result = $query->createCommand()->getSql();
echo $result;
Here is produced query:
SELECT `admins`.* FROM `admins`
LEFT JOIN `person` ON `admins`.`idadm` = `person`.`idadm` LIMIT 1

Load relations only when a field in parent table is true

I have a parent table named Post which has a boolean column named is_anonymous. The Post table has a relation to Users table. I want to load this relation only when is_anonymous set to false. Is there a way I can achieve this?
The below relation gives users for all the posts.
$institute = Institute::where('inst_id', '=', $institute_id)->with(
['posts' => function ($posts) {
$posts->with(['user', 'tags']);
}]
)->orderBy('created_at', 'DESC')->skip($skip)->take(10)->get();
I ended up solving this using lazy loading. I think there is no other way I can do this. Please add answers if you find a better way.
$posts = Institute::where('inst_id', '=', $institute_id)->first()->posts()->with('tags')
->orderBy('created_at', 'DESC')->skip($skip)->take(10)->get();
foreach ($posts as $post) {
if (!$post['is_anonymous']) {
$post->load('user');
}
}
Or,
To be performance friendly, I'm currently solving this using the below query and removing the key user, as iterating over a list of 10 items and removing is always better than making 10 queries (worst case)
$posts = Institute::where('inst_id', '=', $institute_id)->first()->posts()->with(['user', 'tags'])
->orderBy('created_at', 'DESC')->skip($skip)->take(10)->get();
foreach ($posts as $post) {
if ($post['is_anonymous']) {
unset($post['user'])
}
}

Laravel - SELECT only certain columns within a `::with` controller function

The following code I have is working perfectly fine, however, it returns more data than what is necessary from each table:
public function getIndex()
{
$alerts = Criteria::with('bedrooms', 'properties')
->where('user_id', '=', Auth::id())
->get();
$this->layout->content = View::make('users.alert.index',
array('alerts' => $alerts));
}
What I'd like to do is, for example, select only the bedroom column out of the bedroom table. As it stands now, it returns all columns.
I have tried:
public function getIndex()
{
$alerts = Criteria::with('bedrooms' => function($q){
$q->select('bedroom');
}, 'properties')
->where('user_id', '=', Auth::id())
->get();
$this->layout->content = View::make('users.alert.index',
array('alerts' => $alerts));
}
But I am presented with the following error:
syntax error, unexpected '=>' (T_DOUBLE_ARROW)
Any help as to how I can achieve this will be hugely appreciated.
Update
public function getIndex()
{
$alerts = Criteria::with(
['coordinate' => function($w){
$w->select('name', 'id');
}],
['bedrooms' => function($q){
$q->select('bedroom', 'criteria_id');
}]
, 'properties')
->where('user_id', Auth::id())
->get();
$this->layout->content = View::make('users.alert.index',
array('alerts' => $alerts));
}
The correct select query works for which ever is queried first. If I swap the queries around, the bedroom function works correctly, but the rest aren't eager loaded nor does the select query work.
Just pass an array there:
// note [ and ]
$alerts = Criteria::with(['bedrooms' => function($q){
$q->select('bedroom', 'PK / FK');
}, 'properties'])
Also mind that you need to select the keys of that relation (primary key/foreign key of the relation).
The answer to your update question is that you need to eager load other values in the same array some thing like this.
public function getIndex()
{
$alerts = Criteria::with(
['coordinate' => function($w)
{
$w->select('name', 'id');
},
'bedrooms' => function($q)
{
$q->select('bedroom', 'criteria_id');
}
, 'properties'])
->where('user_id', Auth::id())
->get();
$this->layout->content = View::make('users.alert.index',
array('alerts' => $alerts));
}