Home yii2 How to work with Active Record connections in Yii2 (Populatelelation, Link, etc.)?

How to work with Active Record connections in Yii2 (Populatelelation, Link, etc.)?

Author

Date

Category

In June 2014, Habrahabre was article with a description of working with Active Record links in Yii2 . A month later, the article disappeared, but remained in numerous copies, for example, here .

Like some other developers, (see here and zel ) I tried to figure out the example that the author of this article leads. And now for a few days I do not leave me feeling that something in that article is missing.

Normal description of the approach I did not find (except that the backs of Here ). I read out the official manual to the holes, but Recording Takes related models in the database are described very much , Reading Active Record Documentation also not particularly helped.

And yet I want to deal with this code, with this approach, to understand what opportunities are laid in the framework, so as not to burn the garden over the existing one.

code from the article

Model

class post extenscript
{
  // We will use transactions at the specified scenarios
  Public Function TRANSACTIONS ()
  {
    Return [
      Self :: Scenario_Default = & gt; Self :: Op_insert | Self :: OP_UPDATE,
    ];
  }
  Public Function GetTags ()
  {
    Return $ this- & gt; hasmany (tag :: classname (), ['id' = & gt; 'tag_id'])
      - & gt; VIATABLE ('post_tag', ['post_id' = & gt; 'id']);
  }
  Public Function Settags ($ Tags)
  {
    $ this- & gt; populatelelation ('tags', $ tags);
    $ this- & gt; tags_count = count ($ tags);
  }
  // Setter to produce tags from a row separated by a comma
  Public Function SetTagsString ($ Value)
  {
    $ tags = [];
    Foreach (Explode (',' $ Value) AS $ Name) {
       $ tag = new tag ();
       $ Tag- & GT; Name = $ Name;
       $ tags [] = $ tag;
    }
    $ this- & gt; settags ($ tags);
  }
  Public Function GetCover ()
  {
    Return $ this- & gt; Hasone (Image :: classname (), ['id' = & gt; 'cover_id']);
  }
  Public Function Setcover ($ Cover)
  {
    $ this- & gt; populaterelation ('Cover', $ Cover);
  }
  Public Function GetImages ()
  {
    Return $ this- & gt; hasmany (Image :: classname (), ['post_id' = & gt; 'id']);
  }
  Public Function Setimages ($ images)
  {
    $ this- & gt; populaterelation ('images', $ images);
    if (! $ this- & gt; isrelationpopulated ('Cover') & amp; & amp;! $ this- & gt; getcover () - & gt; one ()) {
      $ this- & gt; setcover (reset ($ images));
    }
  }
  Public Function LoadupLoadedImages ()
  {
      $ images = [];
      Foreach (Uploadedfile :: GetInstances (New Image (), 'image') as $ file) {
        $ image = new image ();
        $ image- & gt; name = $ file- & gt; name;
        $ images [] = $ image;
      }
      $ this- & gt; setimages ($ images);
  }
  Public Function Beforesave ($ Insert)
  {
    If (! Parent :: Beforeesave ($ Insert)) {
      RETURN FALSE;
    }
    // in BeforeSave we keep related models
    // who need to be preserved to the main, i.e. need their ID
    // Do not worry about the transaction. We set up
    // It will be started when calling the `Insert () method` and `update ()`
    // Get all the connected models, those downloaded or installed
    $ relatedRecords = $ this- & gt; getRelatedRecords ();
    If (ISSET ($ RelatedRecords ['Cover'])) {
      $ this- & gt; link ('Cover', $ relatedRecords ['Cover']);
    }
    RETURN TRUE;
  }
  Public Function Aftersave ($ Insert) 
{
    // in aftersave we keep related models
    // which need to be saved after the main model, because Need her ID
    // Get all the connected models, those downloaded or installed
    $ relatedRecords = $ this- & gt; getRelatedRecords ();
    if (ISset ($ relatedRecords ['Tags'])) {
      Foreach ($ RelatedRecords ['Tags'] AS $ Tag) {
        $ this- & gt; link ('tags', $ tag);
      }
    }
    If (ISSET ($ RelatedRecords ['images'])) {
      Foreach ($ RelatedRecords ['images'] AS $ Image) {
        $ this- & gt; link ('images', $ image);
      }
    }
  }
}

Controller

class postcontroller extends controller
{
  Public Function ActionCreate ()
  {
    $ post = new post ();
    If ($ post- & gt; load (Yii :: $ App- & gt; Request- & gt; post ())) {
      // Save downloaded files
      $ post- & gt; loaduploadedimages ();
      if ($ post- & gt; save ()) {
        Return $ this- & gt; redirect (['View', 'id' = & gt; $ post- & gt; id]);
      }
    }
    Return $ this- & gt; render ('Create', [
      'post' = & gt; $ post
    ]);
   }
}

Questions on the code:

  1. How do setters work in this example? Where do the values ​​transmitted to the Setters ($ Tags, $ Cover, $ Images …) come from?
  2. At what moment data from related models are written (tags, images, main image) in the database?
  3. What is missing in this code so that it earns?

Separately, I would like to ask links to the repository of serious projects using Yii2. I really want to look at the best practices in real complex projects.


Answer 1, Authority 100%

Overview

In the controller:

$ post- & gt; load (Yii :: $ App- & gt; Request- & gt; post () Performs model loading. Yii :: $ App- & gt; Request- & gt ; post () Returns an array, kind ['MyFormName [Key]' = & gt; 'value] . in Load for each model properties named Key Set the value if they exist for the validation rules Rules and this field is written in the script.

In the model:

GetTags it is relay. Serves for ActiveRecord models connections. settags Although it looks like a setter (setter, this is the function called when accessing a non-existent property), but in this case it is just a function. It saves the associated model using PopulateRelation and increases the current counter $ this- & gt; Tags_Count . By the way about the problem of public properties, the meter can be increased directly, without calling the setTAGS method.


How do setters work in this example? Where do the values ​​transmitted to the Setters ($ Tags, $ Cover, $ Images …)?

setcover , settags , setimages are called inside the model with normal transmission of parameters. I suppose they must be private and not called from the client code (controller).

At what point are the data from related models (tags, images, the main image) are written to the database?

At the time of calling PopulateRelation in the model, the associated relay is filling. Saving occurs in the controller at the time Save models.

What is missing in this code so that it earn?

write all over, using this code only as an example. You need an experience, and it will save you from the need to think about the functions like the settagsstring , which are not used anywiches and bring only confusion. It will also be clear that for the getRelatedRecords method, you must be filled with Tables images and Tags .

I recommend to take the this section of the documentation and turns to walk for each function. It’s a long, all I myself have not yet passed, but it will get the most complete picture of the opportunities ActiveRecord framework, without delving into the code of dubious quality.


Update: Example operating code of this example

.

Despite the fact that this is just a quick refactoring ready kodrevyu and questions in the comments.

Migration:

& lt;? php
USE Yii \ db \ migration;
class m151122_155133_create_tables extends Migration
{
  Public Function Up ()
  {
    $ TableOptions = NULL;
    if ($ this- & gt; db- & gt; drivename === 'mysql') {
      // http://stackoverflow.com/questions/766809/whats-The-Difference-Between-UnF8-General-Ci-D-UF8-Unicode-ci.
      $ TableOptions = 'Character Set UTF8 Collate UTF8_Unicode_ci Engine = Innodb';
    }
    $ This- & gt; createTable ( '{{% post}}', [
      'id' = & gt; $ this- & gt; primarykey (),
      'Message' = & gt; $ This- & gt; text (),
      'Tags_count' = & gt; $ This- & gt; integer (2) - & gt; notNull () - & gt; defaultValue (0)
    ], $ tableoptions);
    $ This- & gt; createTable ( '{{% tag}}', [
      'id' = & gt; $ this- & gt; primarykey (),
      'Name' = & gt; $ this- & gt; string (32) - & gt; notnull (),
      'UNIQUE INDEX `UNQ_tag__name` (` name`)',
    ], $ tableoptions);
    $ This- & gt; batchInsert ( '{{% tag}}', [ 'name'], [
      [ 'Tag1'],
      [ 'Tag2'],
    ]);
    $ This- & gt; createTable ( '{{% post_tag}}', [
      'id' = & gt; $ this- & gt; primarykey (),
      'post_id' = & gt; $ This- & gt; integer (11,) - & gt; notNull (),
      'Tag_id' = & gt; $ This- & gt; integer (11) - & gt; notNull (),
      'FOREIGN KEY `FK_post_tag__post_id` (post_id) REFERENCES post (id)
        ON UPDATE RESTRICT
        ON DELETE RESTRICT ',
      'FOREIGN KEY `FK_post_tag__tag_id` (tag_id) REFERENCES tag (id)
        ON UPDATE RESTRICT
        ON DELETE RESTRICT ',
    ], $ tableoptions);
    $ This- & gt; createTable ( '{{% post_image}}', [
      'id' = & gt; $ this- & gt; primarykey (),
      'post_id' = & gt; $ This- & gt; integer (11) - & gt; notNull (),
      'image' = & gt; $ This- & gt; string (128) - & gt; notNull (),
      'is_cover' = & gt; $ This- & gt; boolean () - & gt; defaultValue (0)
      'FOREIGN KEY `FK_post_image__post_id` (post_id) REFERENCES post (id)
        ON UPDATE RESTRICT
        ON DELETE RESTRICT ',
    ], $ tableoptions);
  }
  Public Function Down ()
  {
    $ This- & gt; dropTable ( '{{% post_image}}');
    $ This- & gt; dropTable ( '{{% post_tag}}');
    $ This- & gt; dropTable ( '{{% tag}}');
    $ This- & gt; dropTable ( '{{% post}}');
  }
}

The controller:

& lt;? php
namespace frontend \ controllers;
use frontend \ models \ CreatePostForm;
use frontend \ models \ Post;
use frontend \ models \ PostSearch;
USE Yii;
use yii \ base \ Exception;
use yii \ web \ Controller;
use yii \ web \ NotFoundHttpException;
/ **
 * PostController implements the CRUD actions for Post model.
 * /
class PostController extends Controller
{
  / **
   * Lists all Post models.
   *
   * @return Mixed
   * /
  public function actionIndex ()
  {
    $ SearchModel = new PostSearch ();
    $ DataProvider = $ searchModel- & gt; search (Yii :: $ app- & gt; request- & gt; queryParams);
    return $ this- & gt; render ( 'index', [
      'SearchModel' = & gt; $ SearchModel,
      'DataProvider' = & gt; $ DataProvider,
    ]);
  }
  / **
   * Displays a single Post model.
   *
   * @Param integer $ id
   *
   * @return Mixed
   * /
  public function actionView ($ id)
  {
    return $ this- & gt; render ( 'view', [
      'Model' = & gt; $ This- & gt; findModel ($ id),
    ]);
  }
  / **
   * Creates a new Post model.
   * If creation is successful, the browser will be redirected to the 'view' page.
   *
   * @return Mixed
   * /
  Public Function ActionCreate ()
  { 
$ Model = New CreatePostform ();
    if ($ model- & gt; load (Yii :: $ App- & gt; Request- & gt; post ()) & amp; & amp; $ model- & gt; validate ()) {
      If (! $ Model- & gt; createnewpost ()) {
        Throw New Exception ('Failed to Save CreatePostForm');
      }
      Return $ this- & gt; redirect (['View', 'id' = & gt; $ model- & gt; ID]);
    }
    Return $ this- & gt; render ('Create', [
      'Model' = & gt; $ MODEL,
    ]);
  }
  / **
   * Finds The Post Model Based on Its Primary Key Value.
   * If The Model Is Not Found, A 404 HTTP Exception Will Be Thrown.
   *
   * @param integer $ id
   *
   * @return Post The Loaded Model
   * @Throws NotFoundHttpException If The Model Cannot Be Found
   * /
  PROTECTED FUNCTION FINDMODEL ($ ID)
  {
    if (($ model = post :: Findone ($ ID)! == NULL) {
      RETURN $ MODEL;
    } else {
      Throw New NotFoundHttpException ('The Requested Page Does Not Exist.');
    }
  }
}

Presentation index.php

& lt;? php
USE Yii \ Helpers \ HTML;
USE Yii \ grid \ gridview;
/ * @var $ this yii \ Web \ View * /
/ * @var $ searchmodel frontend \ model \ postsearch * /
/ * @var $ DataProvider Yii \ Data \ ACTIVEDATAPROVIDER * /
$ this- & gt; title = 'posts';
$ this- & gt; params ['BreadCrumbs'] [] = $ this- & gt; title;
? & gt;
& lt; div class = "POST-INDEX" & gt;
  & lt; h1 & gt; & lt;? = HTML :: ENCODE ($ this- & gt; title)? & gt; & lt; / h1 & gt;
  & lt; p & gt;
    & lt;? = HTML :: A ('CREATE POST', [CREATE '], [' Class' = & GT; 'BTN BTN-SUCCESS'])? & GT;
  & lt; / p & gt;
  & lt;? = GridView :: Widget ([
    'dataprovider' = & gt; $ DataProvider,
    'FilterModel' = & gt; $ searchmodel
    'columns' = & gt; [
      ['Class' = & gt; 'Yii \ grid \ serialcolumn'],
      'ID',
      'Message: NTEXT',
      ['Class' = & gt; 'yii \ grid \ actioncolumn'],
    ],
  ]); ? & gt;
& lt; / div & gt;

View Create.php

& lt;? php
USE Yii \ Helpers \ HTML;
USE Yii \ Widgets \ ActiveForm;
/ * @var $ this yii \ Web \ View * /
/ * @Var $ Model Frontend \ Models \ POST * /
/ * @Var $ Form Yii \ Widgets \ ActiveForm * /
$ this- & gt; title = 'CREATE POST';
$ this- & gt; params ['breadcrumbs'] [] = ['label' = & gt; 'Posts', 'url' = & gt; ['index']];
$ this- & gt; params ['BreadCrumbs'] [] = $ this- & gt; title;
? & gt;
& lt; div class = "post-create" & gt;
  & lt; h1 & gt; & lt;? = HTML :: ENCODE ($ this- & gt; title)? & gt; & lt; / h1 & gt;
  & lt; div class = "post-form" & gt;
    & lt;? php $ Form = ActiveForm :: Begin (['Options' = & gt; ['entype' = & gt; 'multipart / form-data']]); ? & gt;
    & lt;? = $ form- & gt; field ($ model, 'message') - & gt; textarea (['rows' = & gt; 6])? & gt;
    & lt;? = $ form- & gt; field ($ model, 'tagstring') - & gt; input ('text')? & gt;
    & lt;? = $ form- & gt; field ((new \ frontend \ model \ postimage ()), 'image []') - & gt; FileInput (['Multiple' = & gt; true, 'accept' = & gt; ' image / * '])? & gt;
    & lt; div class = "Form-Group" & gt;
      & lt;? = HTML :: SubmitButton ($ model- & gt; IsnewRecord? 'Create': 'Update', ['Class' = & gt; $ model- & gt; IsnewRecord?' BTN BTN-SUCCESS ':' BTN BTN-PRIMARY '])? & gt;
    & lt; / div & gt;
    & lt;? PHP ActiveForm :: End (); ? & gt;
  & lt; / div & gt;
& lt; / div & gt;

View view.php

& lt;? php
USE Yii \ Helpers \ HTML;
USE Yii \ Widgets \ DetailView;
/ * @var $ this yii \ Web \ View * /
/ * @Var $ Model Frontend \ Models \ POST * /
$ this- & gt; title = $ model- & gt; id;
$ this- & gt; params ['breadcrumbs'] [] = ['label' = & gt; 'Posts', 'url' = & gt; ['index']];
$ this- & gt; params ['BreadCrumbs'] [] = $ this- & gt; title;
? & gt;
& lt; div class = "post-view" & gt;
  & lt; h1 & gt; & lt;? = HTML :: ENCODE ($ this- & gt; title)? & gt; & lt; / h1 & gt;
  & lt; p & gt;
    & lt;? = HTML :: A ('Update', ['Update', 'ID' = & gt; $ model- & gt; id], ['class' = & gt; 'BTN BTN-PRIMARY'])? & GT;
    & lt;? = HTML :: A ('Delete', ['Delete', 'id' = & gt; $ model- & gt; id], [
      'Class' = & gt; 'BTN BTN-Danger',
      'Data' = & gt; [
        'confirm' = & gt; 'Are You Sure You Want to Delete this Item?'
        'Method' = & gt; 'Post', 
],
    ])? & gt;
  & lt; / p & gt;
  & lt;? = DetailView :: Widget ([
    'Model' = & gt; $ MODEL,
    'attributes' = & gt; [
      'ID',
      'Message: NTEXT',
    ],
  ])? & gt;
& lt; / div & gt;

Model CreatePostForm

& lt;? php
Namespace Frontend \ Models;
USE Yii;
USE Yii \ Helpers \ ArrayHelper;
Use Yii \ Web \ uploadedfile;
Class CreatePostform EXTENDS POST
{
  / **
   * Store the string here. Private because we need a setter, and we prescribe a getter for the company so that it is impossible to write directly to the property, bypassing the setter.
   *
   * @var string.
   * /
  Private $ _TagString;
  / **
   * Load will work only for those fields for which validation rules are prescribed.
   *
   * @return Array.
   * /
  Public Function Rules ()
  {
    Return ArrayHelper :: Merge ([
      ['TagString', 'String']
    ], Parent :: Rules ());
  }
  / **
   * We try to work directly with ActiveRecord in the controller.
   *
   * @return bool
   * /
  Public Function CreateNewPost ()
  {
    $ this- & gt; loaduploadedimages ();
    Return $ this- & gt; save ();
  }
  / **
   * Just get tags that entered the user on the form
   *
   * @return string.
   * /
  Public Function GetTagString ()
  {
    Return $ this- & gt; _tagstring;
  }
  / **
   * Saving pictures. There is only the name of the names in the database, the file entry is not made.
   *
   * @See http://www.yiiframework.com/doc-2.0/guide-input-file-upload.html
   * /
  Private Function LoadupLoadedImages ()
  {
    $ images = [];
    Foreach (UploadedFile :: GetInstances (New Postimage (), 'image') as $ file) {
      $ image = new postimage ();
      $ image- & gt; image = $ File- & gt; Name;
      $ images [] = $ image;
    }
    $ this- & gt; setimages ($ images);
  }
  / **
   * Satisfy the links of pictures and set the IS_COVER flag for the first picture. Here in the example is just creating, but if there was a update Call Setcover
   * did not happen.
   *
   * @param PostImage [] $ Images
   * /
  Private Function Setimages ($ images)
  {
    $ this- & gt; populaterelation ('images', $ images);
    if (! $ this- & gt; isrelationpopulated ('Cover') & amp; & amp;! $ this- & gt; getcover () - & gt; one ()) {
      $ this- & gt; setcover (reset ($ images));
    }
  }
  / **
   * Keep the main picture of the post.
   *
   * @param PostImage $ Cover
   * /
  Private Function Setcover ($ Cover)
  {
    $ Cover- & gt; is_cover = true;
    $ this- & gt; populaterelation ('Cover', $ Cover);
  }
  / **
   * Record the line of tags received from the user to the associated table.
   *
   * @Param String $ TagString
   * /
  Public Function SetTagString ($ TagString)
  {
    $ this- & gt; _tagstring = $ tagstring;
    $ this- & gt; savetagstorelation ();
  }
  / **
   * Save tags in the associated table and increase the counter
   * /
  Private Function SaveTagStorelation ()
  {
    $ tags = [];
    / **
     * Example with VIATABLE in the POST :: GetTags implied that tags are only selected, but are not created.
     * /
    Foreach (Explode (',', $ this- & gt; _tagstring) AS $ Name) {
      $ Tag = Tag :: Find () - & gt; where '[[' name '= & gt; Trim ($ Name)]) - & gt; one ();
      If (! $ Tag) {
        Continue;
      }
      $ tags [] = $ tag;
    }
    $ this- & gt; populatelelation ('tags', $ tags);
    $ this- & gt; tags_count = count ($ tags);
  }
}

Model Post

& lt;? php
Namespace Frontend \ Models;
USE Yii;
/ **
 * This Is The Model Class for Table "POST".
 *
 * @property integer $ id
 * @Property String $ Message
 * @property integer $ tags_count
 *
 * @Property PostImage $ Image
 * @Property Tag [] $ Tags
 * /
Class Post Extends \ Yii \ db \ ActiveRecord
{
  / **
   * @inheritdoc.
   * /
  Public Static Function Tablename ()
  {
    Return 'Post';
  }
  / **
   * @inheritdoc.
   * /
  Public Function Rules ()
  { 
Return [
      ['tags_count', 'integer'],
      [['Message'], 'String'],
    ];
  }
  / **
   * @inheritdoc.
   * /
  Public Function attributelabels ()
  {
    Return [
      'id' = & gt; 'ID',
      'Message' = & gt; 'Message',
    ];
  }
  / **
   * @inheritdoc.
   * /
  Public Function TRANSACTIONS ()
  {
    Return [
      Self :: Scenario_Default = & gt; Self :: Op_insert | Self :: OP_UPDATE,
    ];
  }
  / **
   * @return \ yii \ db \ ActiveQuery
   * /
  Public Function GetCover ()
  {
    Return $ this- & gt; hasone (Postimage :: classname (), ['post_id' = & gt; 'id'])
      - & gt; andwhere (['IS_COVER' = & GT; TRUE]);
  }
  / **
   * @return \ yii \ db \ ActiveQuery
   * /
  Public Function GetImages ()
  {
    Return $ this- & gt; hasmany (PostImage :: classname (), ['post_id' = & gt; 'id']);
  }
  / **
   * @return \ yii \ db \ ActiveQuery
   * /
  Public Function GetTags ()
  {
    Return $ this- & gt; hasmany (tag :: classname (), ['id' = & gt; 'tag_id'])
      - & gt; VIATABLE ('post_tag', ['post_id' = & gt; 'id']);
  }
  / **
   * In Aftersave, we save the associated models that need to be saved after the main model, because Need her ID.
   *
   * @param bool $ true
   * @param Array $ ChangeDattributes
   * /
  Public Function Aftersave ($ True, $ ChangeDattributes)
  {
    $ relatedRecords = $ this- & gt; getRelatedRecords ();
    If (ISSET ($ RelatedRecords ['Cover'])) {
      $ this- & gt; link ('Cover', $ relatedRecords ['Cover']);
    }
    if (ISset ($ relatedRecords ['Tags'])) {
      Foreach ($ RelatedRecords ['Tags'] AS $ Tag) {
        $ this- & gt; link ('tags', $ tag);
      }
    }
    If (ISSET ($ RelatedRecords ['images'])) {
      Foreach ($ RelatedRecords ['images'] AS $ Image) {
        $ this- & gt; link ('images', $ image);
      }
    }
  }
}

Model Postimage

& lt;? php
Namespace Frontend \ Models;
USE Yii;
/ **
 * This Is The Model Class for Table "post_image".
 *
 * @property integer $ id
 * @Property Integer $ post_id
 * @property String $ Image
 * @property integer $ is_cover
 *
 * @property Post $ post
 * /
Class PostImage EXTENDS \ Yii \ Db \ ActiveRecord
{
  / **
   * @inheritdoc.
   * /
  Public Static Function Tablename ()
  {
    RETURN 'POST_IMAGE';
  }
  / **
   * @inheritdoc.
   * /
  Public Function Rules ()
  {
    Return [
      [['post_id', 'image'], 'required'],
      [['post_id', 'is_cover'], 'integer'],
      [['image'], 'String', 'Max' = & gt; 128],
      [['post_id'], 'unique'],
      [['post_id'], 'exist', 'skiponerror' = & gt; TRUE, 'TARGETCLASS' = & GT; POST :: classname (), 'targetattribute' = & gt; ['post_id' = & gt; 'id']]
    ];
  }
  / **
   * @inheritdoc.
   * /
  Public Function attributelabels ()
  {
    Return [
      'id' = & gt; 'ID',
      'post_id' = & gt; 'POST ID',
      'image' = & gt; 'Image',
      'is_cover' = & gt; 'Is Cover',
    ];
  }
  / **
   * @return \ yii \ db \ ActiveQuery
   * /
  Public Function GetPost ()
  {
    Return $ this- & gt; hasone (Post :: classname (), ['id' = & gt; 'post_id']);
  }
}

Model Posttag

& lt;? php
Namespace Frontend \ Models;
USE Yii;
/ **
 * This Is The Model Class for Table "post_tag".
 *
 * @property integer $ id
 * @Property Integer $ post_id
 * @property integer $ tag_id
 *
 * @property post $ post
 * @Property Tag $ Post0
 * /
Class Posttag Extends \ Yii \ db \ ActiveRecord
{
  / **
   * @inheritdoc.
   * /
  Public Static Function Tablename ()
  {
    Return 'post_tag';
  }
  / **
   * @inheritdoc
   * /
  Public Function Rules ()
  {
    Return [
      [['post_id', 'tag_id'], 'required'],
      [['post_id', 'tag_id'], 'integer'], 
[['post_id'], 'exist', 'skipOnError' = & gt; true, 'targetClass' = & gt; Post :: className (), 'targetAttribute' = & gt; ['post_id' = & gt; 'id']],
      [['tag_id'], 'exist', 'skipOnError' = & gt; true, 'targetClass' = & gt; Tag :: className (), 'targetAttribute' = & gt; ['tag_id' = & gt; 'id']],
    ];
  }
  / **
   * @inheritdoc
   * /
  public function attributeLabels ()
  {
    return [
      'id' = & gt; 'ID',
      'post_id' = & gt; 'Post ID',
      'tag_id' = & gt; 'Tag ID',
    ];
  }
  / **
   * @return \ yii \ db \ ActiveQuery
   * /
  public function getPost ()
  {
    return $ this- & gt; hasOne (Post :: className (), ['id' = & gt; 'post_id']);
  }
  / **
   * @return \ yii \ db \ ActiveQuery
   * /
  public function getPost0 ()
  {
    return $ this- & gt; hasOne (Tag :: className (), ['id' = & gt; 'post_id']);
  }
}

Model Tag

& lt;? php
namespace frontend \ models;
use Yii;
/ **
 * This is the model class for table "tag".
 *
 * @property integer $ id
 * @property string $ name
 *
 * @property PostTag [] $ postTags
 * /
class Tag extends \ yii \ db \ ActiveRecord
{
  / **
   * @inheritdoc
   * /
  public static function tableName ()
  {
    return 'tag';
  }
  / **
   * @inheritdoc
   * /
  public function rules ()
  {
    return [
      [['name'], 'required'],
      [['name'], 'string', 'max' = & gt; 32],
      [['name'], 'unique'],
    ];
  }
  / **
   * @inheritdoc
   * /
  public function attributeLabels ()
  {
    return [
      'id' = & gt; 'ID',
      'name' = & gt; 'Name',
    ];
  }
  / **
   * @return \ yii \ db \ ActiveQuery
   * /
  public function getPostTags ()
  {
    return $ this- & gt; hasMany (PostTag :: className (), ['post_id' = & gt; 'id']);
  }
}

PostSearch

Model

& lt;? php
namespace frontend \ models;
use Yii;
use yii \ base \ Model;
use yii \ data \ ActiveDataProvider;
use frontend \ models \ Post;
/ **
 * PostSearch represents the model behind the search form about `frontend \ models \ Post`.
 * /
class PostSearch extends Post
{
  / **
   * @inheritdoc
   * /
  public function rules ()
  {
    return [
      [['id'], 'integer'],
      [['message'], 'safe'],
    ];
  }
  / **
   * @inheritdoc
   * /
  public function scenarios ()
  {
    // bypass scenarios () implementation in the parent class
    return Model :: scenarios ();
  }
  / **
   * Creates data provider instance with search query applied
   *
   * @param array $ params
   *
   * @return ActiveDataProvider
   * /
  public function search ($ params)
  {
    $ query = Post :: find ();
    // add conditions that should always apply here
    $ dataProvider = new ActiveDataProvider ([
      'query' = & gt; $ query,
    ]);
    $ this- & gt; load ($ params);
    if (! $ this- & gt; validate ()) {
      // uncomment the following line if you do not want to return any records when validation fails
      // $ query- & gt; where ('0 = 1');
      return $ dataProvider;
    }
    // grid filtering conditions
    $ query- & gt; andFilterWhere ([
      'id' = & gt; $ this- & gt; id,
    ]);
    $ query- & gt; andFilterWhere (['like', 'message', $ this- & gt; message]);
    return $ dataProvider;
  }
}

Answer 2, authority 23%

How do setters work in this example? Where do the values ​​passed to the setters ($ tags, $ cover, $ images …) come from?

This is PHP magic. The base class has overridden the __get and __set methods, which convert the reading and writing of $ foo- & gt; bar = $ baz- & gt; qwe attributes to method calls $ foo- & gt; setBar ($ baz- & gt; getQwe ()) . If you don’t like magic, you can call methods directly.

In essence, these are banal getters and setters. In some languages, there are properties (getters and setters combined into a single essence), but PHP does not support them.

At what point are the data from related models (tags, images, the main image) are written to the database?

When the Save ActiveRecord is called. At this point, the data is valid and if the validation passes, then the data of this ActiveRecord and all connected are written.

What is missing in this code so that it earns?

This question cannot be answered until you say that it is not working.

You can see comments, there is some kind of bug: https://sohabr.net/habr // POST / 226103 /

Apparently, this article has not enjoyed popularity, so I do not understand the desire to rely on it in training.

Separately, I would like to ask links to the repository of serious projects using Yii2. I really want to look at the best practices in real complex projects.

There is an example of Yii2-Shop from one of the authors of the framework.

Programmers, Start Your Engines!

Why spend time searching for the correct question and then entering your answer when you can find it in a second? That's what CompuTicket is all about! Here you'll find thousands of questions and answers from hundreds of computer languages.

Recent questions