Laravel relationship : relationship that does not return me what I ask - relationship

rI am new to laravel 7 and I still have a few small difficulties.
I have two tables: users and services. These two tables have a relation to retrieve a user's service.
$users= User::with('poseur')->get();
It returns all users even those who do not meet the conditions of my relationship.
I use scope in service model:
public function scopePoseurs(){
return $query->whereRaw('slug','pos')
}
And i use belongsTo relation in user model :
public function poseur(){
return $this->belongsTo('App\Service')->poseurs();
}
Exemple: we hase 2 users:
first: Daniel have service slug = 'pos',
second: Patrick have service slug ='dev'
When i use $users=User::with('poseur')->get();, i see Daniel and Patrick.
While there should be only Daniel.
Can you help me understand ?
Thanks !

with() is for eager loading. That basically means, along the main model, Laravel will preload the relationship(s) you specify. This is especially helpful if you have a collection of models.
If you want to return all user, that has relation with poseur only, then use has() method :
$users= User::has('poseur')->get();
Ther is also a method called whereHas(), which allows you to specify additional filters for the related model to check :
$users = User::whereHas('poseur', function($q){
$q->where('created_at', '>=', '2020-01-01 00:00:00');
})->get();

Related

Cakephp 3 - How to integrate external sources in table?

I working on an application that has its own database and gets user information from another serivce (an LDAP is this case, through an API package).
Say I have a tables called Articles, with a column user_id. There is no Users table, instead a user or set of users is retrieved through the external API:
$user = LDAPConnector::getUser($user_id);
$users = LDAPConnector::getUsers([1, 2, 5, 6]);
Of course I want retrieving data from inside a controller to be as simple as possible, ideally still with something like:
$articles = $this->Articles->find()->contain('Users');
foreach ($articles as $article) {
echo $article->user->getFullname();
}
I'm not sure how to approach this.
Where should I place the code in the table object to allow integration with the external API?
And as a bonus question: How to minimise the number of LDAP queries when filling the Entities?
i.e. it seems to be a lot faster by first retrieving the relevant users with a single ->getUsers() and placing them later, even though iterating over the articles and using multiple ->getUser() might be simpler.
The most simple solution would be to use a result formatter to fetch and inject the external data.
The more sophisticated solution would a custom association, and a custom association loader, but given how database-centric associations are, you'd probably also have to come up with a table and possibly a query implementation that handles your LDAP datasource. While it would be rather simple to move this into a custom association, containing the association will look up a matching table, cause the schema to be inspected, etc.
So I'll stick with providing an example for the first option. A result formatter would be pretty simple, something like this:
$this->Articles
->find()
->formatResults(function (\Cake\Collection\CollectionInterface $results) {
$userIds = array_unique($results->extract('user_id')->toArray());
$users = LDAPConnector::getUsers($userIds);
$usersMap = collection($users)->indexBy('id')->toArray();
return $results
->map(function ($article) use ($usersMap) {
if (isset($usersMap[$article['user_id']])) {
$article['user'] = $usersMap[$article['user_id']];
}
return $article;
});
});
The example makes the assumption that the data returned from LDAPConnector::getUsers() is a collection of associative arrays, with an id key that matches the user id. You'd have to adapt this accordingly, depending on what exactly LDAPConnector::getUsers() returns.
That aside, the example should be rather self-explanatory, first obtain a unique list of users IDs found in the queried articles, obtain the LDAP users using those IDs, then inject the users into the articles.
If you wanted to have entities in your results, then create entities from the user data, for example like this:
$userData = $usersMap[$article['user_id']];
$article['user'] = new \App\Model\Entity\User($userData);
For better reusability, put the formatter in a custom finder. In your ArticlesTable class:
public function findWithUsers(\Cake\ORM\Query $query, array $options)
{
return $query->formatResults(/* ... */);
}
Then you can just do $this->Articles->find('withUsers'), just as simple as containing.
See also
Cookbook > Database Access & ORM > Query Builder > Adding Calculated Fields
Cookbook > Database Access & ORM > Retrieving Data & Results Sets > Custom Finder Methods

How does one get properties from related table as properties of it's own table in Laravel 5?

The question might sound a little bit confusing but I don't know how to explain it better in one sentence.
This describes basically what the problem is:
I have a Users table which can contain 2 types of users. I know how I can separate by role. But here's the thing, users with role 1(editor_in_chief) have different attributes than users with role 2(reviewer).
My idea was to create a table named 'reviewer_attributes' and 'editor_in_chief_attributes' and create a one-to-one relation with this table to hold the attributes for the users table.
Maybe you have a better idea, that would be great as well. But for this scenario, I would like to know if it is possible to make a call to the database and to get these users' properties from the other table as properties of the User object.
When using a DB call using relations laravel will give me something like this:
user {
id: 1,
name: "Name",
reviewer_attributes: {
attribute_1: 'attribute_1',
attribute_2: 'attribute_2',
attribute_3: 'attribute_3',
}
}
But this is what I want to object to obtain look like:
user {
id: 1,
name: "Name",
attribute_1: 'attribute_1',
attribute_2: 'attribute_2',
attribute_3: 'attribute_3',
}
I want to achieve this by a single database call instead of setting the properties after the call.
I find this a very interesting topic, I hope you could help!
If I got your problem right, you may call somthing like this:
DB::table('users')
->join('reviewer_attributes', 'users.id', '=', 'reviewer_attributes.user_id')
->find($id);
you may add select to get specific attributes of each table:
DB::table('users')
->join('reviewer_attributes', 'users.id', '=', 'reviewer_attributes.user_id')
->select('users.id', 'users.name', 'reviewer_attributes.*')
->find($id);
Update: You can also use collections to restructure your results returned by Eloquent:
$result = User::with('reviewerAttributes')->find($id);
$result = $result->get('reviewer_attributes')
->merge($result->forget('reviewer_attributes')->all())
->all();
You need export model to Json?
If so, override toArray method
public function toArray()
{
$arr = parent::toArray();
$reviewer_attributes = $this->getReviewerAttributesSomeHow();
return array_merge($arr, $reviewer_attributes);
}

Sequelize 2 Many to Many relationship

I have videos that can be liked by users and commented by users so I need 2 many to many associations.
User model
User.belongsToMany(models.Video,{ through: 'user_like_video' });
User.belongsToMany(models.Video, { through: 'user_comment_video' });
Video model
Video.belongsToMany(models.User, {through: 'user_like_video'});
Video.belongsToMany(models.User, {through: 'user_comment_video'});
My resulting schema in DB is :
The many to many relationship between video a tags work well I can use the method getTags() to retrieve all the tags from a video but how are created methods when you have 2 (many to many relationship) ?
When I use user.getVideos(), I only get the last relation registered which is here user_comment_video.
I finally find a way :
Video.belongsToMany(models.User, {through: 'user_like_video', as: 'Like'});
Video.belongsToMany(models.User, {through: 'user_comment_video',as: 'Comment'});
this exposes methods getLike() and getComment()

Eager loading same model multiple times in a model

I have a Model named Player with 'id' and 'shortname'.
I have another Model named Team with 'id', 'teamname', 'player_1_id', 'player_2_id' and 'player_3_id'.
I am trying to use relations:
// in Model Team
public function players()
{
return $this->hasOne('App\Player', 'id','player_1_id')
->hasOne('App\Player', 'id','player_2_id')
->hasOne('App\Player', 'id','player_3_id');
}
// In controller
$resource = Team::with('players')->get(); doesnt work.
In this case, Which is the best(fastest) way to use eagerloading?
Thanks in advance...
You should read about database normalization. It does not make sense to store player_1_id, player_2_id and player_3_id in teams database. What if team will contain 20 players? You will create another fields?
You should either add team_id into Player model (in case players always belongs to only one team) or create extra table where you will store connections between players and teams.

Joining 3 tables in Cakephp

Using Cakephp 2.5.3 ... I have the following tables:
transactions (belongs to methods and deliveries)
id
delivery_id
method_id
methods (has many transactions)
id
name
deliveries (has many transactions)
id
date
In my delivery view I would like to see the method name for each delivery.
foreach ($deliveries as $delivery) {
echo $method['name'];
}
( a similar unanswered question is
here:)
I am (obv.) very new to Cakephp, What approach should I take to go about this? Thanks!
=========UPDATE==============
I ended up adding methods to the deliveries controller
$this->set('methods', $this->Method->find('all', array('recursive' => -1)));
And looped through the methods in my (read only) view :
//filtered method array for
$method['id'] == $delivery['Transaction']['0']['method_id'])
// got method name
$button_text = $method['name'];
It works fine but can anyone tell me if this may cause problems for me down the line?
use has many assotiations in Transaction model as -
public $hasMny = array('Method', 'Delivery');
then fetch them -
$this->set(
'methods',
$this->Method->find('all', array('contain' => array('Method', 'Delivery')))
);
you will get all the related result together.

Resources