yii2eager-loadingdatabase-relations

Eager loading of data (Page->PageContent->Documents) in Yii2


I'm stuck with my code. What I'm expect to have is:

I have 3 models (Page, PageContent, Documents). Page is "container" for PageContent's and each PageContent can contain Documents (id's of Documents are stored ar comma-seperated values in PageContent's documents_id).

So, the structure could look like that:

-- Page 1
---- PageContent 1
------ Document 1
------ Document 2
---- PageContent 2
------ Document 3
------ Document 4

Page model:

    public function getPageContents()
    {
        return $this->hasMany(PageContent::className(), ['page_id' => 'id']);
    }

    // Both below - obviously don't work
    public function getDocuments()
    {
        return $this->hasMany(Documents::className(), ['id' => 'documents_id']);
    }

    public function getDocumentFiles()
    {
        return $this->hasMany(Documents::classname());
    }

PageController:

    public function actionView($id)
    {
        $model = Page::find()->where(['id' => $id])->with('pageContents', 'documents')->all();

        return $this->render('view', [
            'model' => $this->findModel($id),
        ]);
    }

And view:

        <?php foreach ($model->pageContents as $content): ?>
        <li>
            <h3>Content ID: <?= $content->id ?></h3>
            <p><strong>Content: "<?= $content->content ?>"</strong></p>
            <p><strong>Documents:</strong></p>
            <ul>
                // This only gives me entered values, but not the documents itself
                <?php foreach (explode(",", $content->documents_id) as $doc): ?>
                    <?= $doc ?>
                <?php endforeach ?>
            </ul>
        </li>
        <?php endforeach ?>

TL;TD: I can output id's that are entered/selected in backend, but can't show real document info ($document->title, $document->file)


Solution

  • Use via() to define the relation via a junction relation:

    // ...
    
    use yii\db\ActiveQuery;
    
    // ...
    
        public function getDocuments(): ActiveQuery
        {
            return $this->hasMany(Documents::class, ['page_content_id' => 'id'])
                ->via('pageContents');
        }
    
    // ...
    
    

    You should have a page_content_id column in the table where you store your Documents, which would link each Document instance to the PageContent entry that the Document belongs to.