Sequelize 2 Many to Many relationship - database

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()

Related

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

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();

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

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.

Sequelize.js many to many eager loading

I have 2 models: User and Team
There are multiple kinds of users (in this case Mentors and Moderators) which are differentiated using an attribute in the User model(). The associations between User and Team are as below:
User.hasMany(models.Team, {as: 'Mentors', through: models.TeamMentor, foreignKey: 'mentorId'});
User.hasMany(models.Team, {as: 'Moderators', through: models.TeamModerator, foreignKey: 'moderatorId'});
Team.hasMany(models.User, {through: models.TeamMentor, foreignKey: 'teamId'});
Team.hasMany(models.User, {through: models.TeamModerator, foreignKey: 'teamId'});
Now I am trying to get the details of the team along with separate objects for all the mentors and moderators that are assigned to the teams. I came to know about the getters and setters for many to many relationships from the documentation but I am not sure how to use the method since there are two different kinds of associations between two models here:
Team - Mentor (User)
Team - Moderator (User)
How to correctly query for a team's details in this case?
PS: TeamMentor and TeamModerator are empty models to help the many to many joins
I'm assuming you have N:M relationships between all those models? Like this:
User ---mentors many---> Teams
User --moderates many--> Teams
Team --is moderated by many--> Users
Team --is mentored by many---> Users
If so, you might want to use the as option not only in the User.hasMany(Team) that you already have, but also in the Team.hasMany(User):
Team.hasMany(models.User, {as: 'mentors', through: models.TeamMentor, foreignKey: 'teamId'});
Team.hasMany(models.User, {as: 'moderators', through: models.TeamModerator, foreignKey: 'teamId'});
I think you can then eager load some or all of them during a query like so:
Team.findAll({
where: {...},
include: [
{model: User, as: `moderators` },
{model: User, as: `mentors` }
// or just use this to include everything:
// include: [{all:true}]
]
}).then(function(teams) {
console.log(JSON.stringify(teams));
// oneTeam.moderators: [array of User]
// oneTeam.mentors: [array of User]
});
(side note: I think your User objects currently have a 'getMentors()' method, and a 'mentors' property, that is an array of Team. Same for the 'moderators' property. Perhaps renaming that to 'mentoredTeams' and 'moderatedTeams' would make it clearer?)

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