5
votes

Trying to load all posts from users a user is following in a "Cakey" way.

Three tables:

users (id, username, password)

posts (id, text, user_id, created)

follows (id, user_id, following_id)

I think my relationships are set up correctly, so I can load a users followers and the people following them:

Users Table

    $this->belongsToMany('Following', [
        'className' => 'Users',
        'foreignKey' => 'user_id',
        'targetForeignKey' => 'following_id',
        'joinTable' => 'follows'
    ]);

    $this->belongsToMany('Followers', [
        'className' => 'Users',
        'foreignKey' => 'following_id',
        'targetForeignKey' => 'user_id',
        'joinTable' => 'follows'
    ]);

Follows Table

    $this->belongsTo('Users', [
        'foreignKey' => 'user_id',
        'joinType' => 'INNER'
    ]);

Posts Table

    $this->belongsTo('Users', [
        'foreignKey' => 'user_id',
        'joinType' => 'INNER'
    ]);

Now struggling to select all the posts from the users a single user follows. Think I need to use the matching() method documented here: Filtering by Associated Data Via Matching And Joins but don't seem to be getting it right.

Trying something along these lines:

$user_id = $this->Auth->user('id');

$posts = $this->Posts->find()->matching('Users.Following', function ($q)  use ($user_id)  {
      return $q->where(['Users.id' => $user_id]);
})->all();

UPDATE:

I think I have my relationships set up correctly, this find will return the user, along with all the users they are following:

  $users = $this->Posts->Users->get(1, [
      'contain' => ['Following']
  ]);
2
Put your find() query here - Rayann Nayran
Added to the question. I think I'm going along the right lines... - Kyle Goslan
What is the result you are getting? - Rayann Nayran
Please, add to your question the three table relationships separately. You add one but you did not tell which table they belongs - Rayann Nayran
I've added my relationship details - Kyle Goslan

2 Answers

0
votes

You missed to inherit $user_id to the matching anonymous function.

$user_id = $this->Auth->user('id');

$posts = $this->Posts->find()
    ->matching('Users.Following', function ($q) use ($user_id) {
          return $q->where(['Users.id' => $user_id]);
    })
    ->all();

Closures may also inherit variables from the parent scope. Any such variables must be passed to the use language construct. From PHP 7.1, these variables must not include superglobals, $this, or variables with the same name as a parameter.

See also Anonymous functions

UPDATE:

Add to Users Table

$this->hasMany('Posts', [
   'className' => 'Posts',
   'foreignKey' => 'user_id'
]);

Controller

$users = $this->Posts->Users->get(1, [
    'contain' => ['Following.Posts']
]);
0
votes

As you mentioned in a comment 'Ideally, I want just an array of post entities which comprises of the posts from the users the user is following.' A simple find query with proper join conditions should do the work :

 $user_id = $this->Auth->user('id');

 $this->loadModel("Posts");
 $posts = $this->Posts->find('all')
            ->select()
            ->where(["users.id" => $user_id])
            ->join([
                'follows' => [
                    'table' => 'Follows',
                    'type' => 'LEFT',
                    'conditions' => 'follows.following_id = Posts.user_id'
                ],
                'users' => [
                    'table' => 'Users',
                    'type' => 'LEFT',
                    'conditions' => 'users.id = follows.user_id'
                ]
            ]);

$posts->toArray();

Note: I haven't used any belongsToMany,belongsTo relationships you defined in models

I hope this will solve your problem.