I’m trying to get a many-to-may relationship to work, but I can’t get it to work.
I have a database with 3 tables: users
, favorites
& videos
. The favorites table contains 2 foreign keys
that point to the users table and the videos table.
Currently I have the following:
User Model:
public function getVideos() { return $this->hasMany(Video::class, ['id' => 'video_id'])->viaTable(UserFavorites::tableName(), ['favorite_id' => 'id']); }
Video Model:
public function getUsers() { return $this->hasMany(User::class, ['id' => 'user_id'])->via(UserFavorites::tableName()); }
VideoController: (ActiveController)
public function actions() { $actions = parent::actions(); $actions['index']['prepareDataProvider'] = [$this, 'prepareDataProvider']; return $actions; } public function prepareDataProvider() { $user = User::findOne('1=1'); return new ActiveDataProvider([ 'query' => $user->getVideos() ]); }
Following what they did in:
Many-to-many relation in yii2 activedataprovider
but that did not work. I also tried with extraFields
and expand
with no luck.
How do I make it so the result will look sorta like this:
[{ "id": 1, "name": "video name", "likes": 69, "user": [{ "id": 1, "name": "John" }] }, ... ]
Advertisement
Answer
You need to update the code in a few places.
The REST
active controller.
It looks like you are trying to get one video with the user’s that liked it, you should be using the view action, not the index action, that should be used to get a list of videos, you can use the base view
action, no need to override it, it will work like you need it.
In the controller, should be enough with setting the model.
use yiirestActiveController; class VideoController extends ActiveController { public $modelClass = Video::class; }
Consider setting $_verbs
and actionOptions
if you need them to not be the default.
The model.
You should use extraFields
to expose the method as a field.
use yiidbActiveRecord; class Video extends ActiveRecord { // Other methods here... public function getUsers() { return $this->hasMany(User::class, ['id' => 'user_id']) ->viaTable(UserFavorites::tableName(), [ 'video_id' => 'id' ]); } public function extraFields() { return [ 'users' ]; } }
If you want to use the same name for the API call as the function, i.e. the API call uses expand=users
and the method is called getUsers()
you can use the users
shortcut on expandFields
, if the names are different, you can use the expanded format, i.e. 'likes' => 'users'
Then, querying your API with:
https://apiv1.example.com/videos/45?expand=users
Should give you the expected results like in your post
[{ "id": 1, "name": "video name", "likes": 69, "users": [{ "id": 1, "name": "John" }] }, ... ]
Notice that the array of users will be called ‘users’ and not ‘user’ if you configured the same way, you can change that using the expanded notation inside extraFields
.