Skip to content
Advertisement

How make a many-to-many rest API in yii2

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.

User contributions licensed under: CC BY-SA
4 People found this is helpful
Advertisement