{"id":15169500,"url":"https://github.com/yii2tech/content","last_synced_at":"2025-10-01T02:31:23.007Z","repository":{"id":57087042,"uuid":"98649585","full_name":"yii2tech/content","owner":"yii2tech","description":"Content management system for Yii2","archived":true,"fork":false,"pushed_at":"2019-07-03T11:11:12.000Z","size":65,"stargazers_count":54,"open_issues_count":0,"forks_count":5,"subscribers_count":7,"default_branch":"master","last_synced_at":"2024-09-22T22:01:58.134Z","etag":null,"topics":["content","management","override","template","yii","yii2","yii2-extension"],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/yii2tech.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"github":["klimov-paul"],"patreon":"klimov_paul"}},"created_at":"2017-07-28T12:57:27.000Z","updated_at":"2023-10-07T23:58:42.000Z","dependencies_parsed_at":"2022-08-20T15:31:12.534Z","dependency_job_id":null,"html_url":"https://github.com/yii2tech/content","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yii2tech%2Fcontent","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yii2tech%2Fcontent/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yii2tech%2Fcontent/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yii2tech%2Fcontent/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yii2tech","download_url":"https://codeload.github.com/yii2tech/content/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":219875269,"owners_count":16554660,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["content","management","override","template","yii","yii2","yii2-extension"],"created_at":"2024-09-27T07:02:30.266Z","updated_at":"2025-10-01T02:31:17.617Z","avatar_url":"https://github.com/yii2tech.png","language":"PHP","funding_links":["https://github.com/sponsors/klimov-paul","https://patreon.com/klimov_paul"],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://github.com/yii2tech\" target=\"_blank\"\u003e\n        \u003cimg src=\"https://avatars2.githubusercontent.com/u/12951949\" height=\"100px\"\u003e\n    \u003c/a\u003e\n    \u003ch1 align=\"center\"\u003eContent management system for Yii2\u003c/h1\u003e\n    \u003cbr\u003e\n\u003c/p\u003e\n\nThis extension provides content management system for Yii2.\n\nFor license information check the [LICENSE](LICENSE.md)-file.\n\n[![Latest Stable Version](https://poser.pugx.org/yii2tech/content/v/stable.png)](https://packagist.org/packages/yii2tech/content)\n[![Total Downloads](https://poser.pugx.org/yii2tech/content/downloads.png)](https://packagist.org/packages/yii2tech/content)\n[![Build Status](https://travis-ci.org/yii2tech/content.svg?branch=master)](https://travis-ci.org/yii2tech/content)\n\n\nInstallation\n------------\n\nThe preferred way to install this extension is through [composer](http://getcomposer.org/download/).\n\nEither run\n\n```\nphp composer.phar require --prefer-dist yii2tech/content\n```\n\nor add\n\n```json\n\"yii2tech/content\": \"*\"\n```\n\nto the require section of your composer.json.\n\n\nUsage\n-----\n\nThis extension provides basic content management system for Yii2.\nThere is a common task to provide ability for application administrator to change site content, like static\npages ('About us', 'How it works' and so on), email templates and so on. This task is usually solved by developer\nusing a dedicated database entities (tables) to store static page or email template contents. However using just\ndatabase entities creates a several problems like defining default (pre-filled) data or updating existing application.\nIn case you need to setup a default pre-set for static pages, so the deployed application does not look empty or\nadd extra static page to the existing site with pre-filled content, you'll need to manipulate database records using\nDB migration mechanism or something. This is not very practical - it would be better to be able to control list of\nstatic pages or email templates inside program code under the VSC (Git, Mercurial etc).\n\nThis extension solves the content management task using 'override' principle: the default set of content is defined by\nthe source code files, while there is an ability to override default contents using database storage.\n\nThis extension provides a special Yii application component - [[\\yii2tech\\content\\Manager]], which provides high level\ninterface for content management. Manager operates by 2 content storages:\n\n - [[\\yii2tech\\content\\Manager::$sourceStorage]] - uses project source files as a default contents source\n - [[\\yii2tech\\content\\Manager::$overrideStorage]] - uses DBMS storage to override source contents\n\nApplication configuration example:\n\n```php\nreturn [\n    'components' =\u003e [\n        'pageContentManager' =\u003e [\n            'class' =\u003e 'yii2tech\\content\\Manager',\n            'sourceStorage' =\u003e [\n                'class' =\u003e 'yii2tech\\content\\PhpStorage',\n                'filePath' =\u003e '@app/data/pages',\n            ],\n            'overrideStorage' =\u003e [\n                'class' =\u003e 'yii2tech\\content\\DbStorage',\n                'table' =\u003e '{{%Page}}',\n                'contentAttributes' =\u003e [\n                    'title',\n                    'body',\n                ],\n            ],\n        ],\n    ],\n    // ...\n];\n```\n\nIn this example default contents for the static pages located in the project files under directory '@app/data/content'.\nEach record is represented by the separated file, like following:\n\n```php\n\u003c?php\n// file '@app/data/pages/about.php'\nreturn [\n    'title' =\u003e 'About',\n    'body' =\u003e 'About page content',\n];\n```\n\nThe override contents will be stored in the database table 'Page', which can be created using following DB migration:\n\n```php\nclass m??????_??????_createPage extends yii\\db\\Migration\n{\n    public function safeUp()\n    {\n        $tableName = 'Page';\n        $columns = [\n            'id' =\u003e $this-\u003estring(),\n            'title' =\u003e $this-\u003estring(),\n            'body' =\u003e $this-\u003etext(),\n            'PRIMARY KEY([[id]])',\n        ];\n        $this-\u003ecreateTable($tableName, $columns);\n    }\n\n    public function safeDown()\n    {\n        $this-\u003edropTable('Page');\n    }\n}\n```\n\nDuring the 'About' page rendering you should use abstraction provided by [[\\yii2tech\\content\\Manager]].\nMethod [[\\yii2tech\\content\\Manager::get()]] returns the content set for particular entity (e.g. particular page),\nholding all related content parts: 'title', 'body' and so on.\nFor example:\n\n```php\n\u003c?php\n// file '@app/views/site/about.php'\n\nuse yii\\bootstrap\\Html;\n\n/* @var $this yii\\web\\View */\n/* @var $contentManager yii2tech\\content\\Manager */\n\n$contentManager = Yii::$app-\u003eget('pageContentManager');\n$contentItem = $contentManager-\u003eget('about');\n\n$this-\u003etitle = $contentItem-\u003erender('title');\n?\u003e\n\u003cdiv class=\"site-about\"\u003e\n    \u003c?= $contentItem-\u003erender('body') ?\u003e\n\u003c/div\u003e\n```\n\nIn case there is a record in 'Page' table with 'id' equal to 'about', the data from this record will be rendered,\notherwise the data from '@app/data/content/about.php' file will be used.\n\nThis extension provides several implementations for the content storage:\n\n - [[yii2tech\\content\\PhpStorage]] - uses a PHP code files for content storage.\n - [[yii2tech\\content\\JsonStorage]] - uses a files in JSON format for content storage.\n - [[yii2tech\\content\\DbStorage]] - uses a relational database as a content storage.\n - [[yii2tech\\content\\MongoDbStorage]] - uses MongoDB as a content storage.\n - [[yii2tech\\content\\ActiveRecordStorage]] - uses ActiveRecord classes for the content storage.\n\nPlease refer to the particular storage class for more details.\n\n\u003e Note: you can use any combination of implementations for 'source' and 'override' storages, but in order to\n  make sense in 'content override' approach storage based on project files (e.g. `PhpStorage` or `JsonStorage`)\n  should be used  for the 'source'.\n\n\n## Template rendering \u003cspan id=\"template-rendering\"\u003e\u003c/span\u003e\n\nStoring just a final HTML content usually is not enough. For the most cases content management operates by content\ntemplates, which will be populated by particular data at runtime. For example: you may want to use application base\nURL inside the 'About' page content, allowing to refer images and create links. Thus default content will look like\nfollowing:\n\n```php\n\u003c?php\n// file '@app/data/pages/about.php'\nreturn [\n    'title' =\u003e 'About',\n    'body' =\u003e \u003c\u003c\u003cHTML\n\u003ch1\u003eAbout page content\u003c/h1\u003e\n\u003cimg src=\"{{appBaseUrl}}/images/about.jpg\"\u003e\n\u003ca href=\"{{appBaseUrl}}\"\u003eHome page\u003c/a\u003e\n...\nHTML\n];\n```\n\nWhile displaying the content, you can pass render parameters to [[\\yii2tech\\content\\Item::render()]] in the same way\nas for regular view rendering:\n\n```php\n\u003c?php\n// file '@app/views/site/about.php'\n\nuse yii\\bootstrap\\Html;\n\n/* @var $this yii\\web\\View */\n/* @var $contentManager yii2tech\\content\\Manager */\n\n$contentManager = Yii::$app-\u003eget('pageContentManager');\n$contentItem = $contentManager-\u003eget('about');\n\n$this-\u003etitle = $contentItem-\u003erender('title');\n?\u003e\n\u003cdiv class=\"site-about\"\u003e\n    \u003c?= $contentItem-\u003erender('body', [\n        'appBaseUrl' =\u003e Yii::$app-\u003erequest-\u003ebaseUrl\n    ]) ?\u003e\n\u003c/div\u003e\n```\n\nYou may setup the content renderer via [[\\yii2tech\\content\\Manager::$renderer]].\nThis extension provides several implementations for the content renderer:\n\n - [[yii2tech\\content\\PlaceholderRenderer]] - performs content rendering via simple string placeholder replacement.\n - [[yii2tech\\content\\PhpEvalRenderer]] - performs content rendering evaluating it as a PHP code.\n - [[yii2tech\\content\\MustacheRenderer]] - performs content rendering using [Mustache](https://mustache.github.io/).\n\nPlease refer to the particular renderer class for more details.\n\n\u003e Note: the actual content template syntax varies depending on actual renderer used.\n\nYou may use [[\\yii2tech\\content\\Manager::$defaultRenderData]] to setup default render data to be used for every rendering,\nso you do not need to pass them all the time:\n\n```php\nreturn [\n    'components' =\u003e [\n        'pageContentManager' =\u003e [\n            'class' =\u003e 'yii2tech\\content\\Manager',\n            'defaultRenderData' =\u003e function () {\n                return [\n                    'appName' =\u003e Yii::$app-\u003ename,\n                    'appBaseUrl' =\u003e Yii::$app-\u003erequest-\u003ebaseUrl,\n                ];\n            },\n            // ...\n        ],\n    ],\n    // ...\n];\n```\n\n\n## Overriding content \u003cspan id=\"overriding-content\"\u003e\u003c/span\u003e\n\nYou may save content override using [[\\yii2tech\\content\\Manager::save()]]. It will write the data into the\n'overrideStorage', keeping the one in 'sourceStorage' intact. For example:\n\n```php\n/* @var $contentManager yii2tech\\content\\Manager */\n$contentManager = Yii::$app-\u003eget('pageContentManager');\n\n$contentManager-\u003esave('about', [\n    'title' =\u003e 'Overridden Title',\n    'body' =\u003e 'Overridden Body',\n]);\n\necho $contentManager-\u003eget('about')-\u003erender('title'); // outputs 'Overridden Title'\n```\n\n\u003e Note: [[\\yii2tech\\content\\Manager]] does NOT perform any check for content parts name matching\n  between source content and overridden one. It is your responsibility to maintain consistence between\n  source and overridden contents set. However, you can use [[\\yii2tech\\content\\Item]] methods (see below)\n  to solve this task.\n\nYou can use [[\\yii2tech\\content\\Manager::reset()]] method in order to restore original (default) value\nfor particular content set. For example:\n\n```php\n/* @var $contentManager yii2tech\\content\\Manager */\n$contentManager = Yii::$app-\u003eget('pageContentManager');\n\n$contentManager-\u003ereset('about');\n\necho $contentManager-\u003eget('about')-\u003erender('title'); // outputs 'About'\n```\n\nYou can perform same data manipulations using interface provided by [[\\yii2tech\\content\\Item]].\nEach content part is accessible via its virtual property with the same name.\nFor example:\n\n```php\n/* @var $contentManager yii2tech\\content\\Manager */\n$contentManager = Yii::$app-\u003eget('pageContentManager');\n\n$contentItem = $contentManager-\u003eget('about');\n$contentItem-\u003etitle = 'Overridden Title';\n$contentItem-\u003ebody = 'Overridden Body';\n$contentItem-\u003esave(); // saves override\n\n$contentItem-\u003ereset(); // restores origin (source)\n```\n\nUsage of [[\\yii2tech\\content\\Item]] solves the problem of verification of matching source and override\ncontents set.\n\n\n## Saving extra content \u003cspan id=\"saving-extra-content\"\u003e\u003c/span\u003e\n\nYou can add a completely new contents set into 'override' storage, even if it has no match in 'source' one.\nThere is no direct restriction for that. For example:\n\n```php\n/* @var $contentManager yii2tech\\content\\Manager */\n$contentManager = Yii::$app-\u003eget('pageContentManager');\n\n$contentManager-\u003esave('newPage', [\n    'title' =\u003e 'New page',\n    'body' =\u003e 'New page body',\n]);\n\necho $contentManager-\u003eget('newPage')-\u003erender('title'); // outputs 'New page'\n\n$contentManager-\u003ereset('newPage');\n\n$contentManager-\u003eget('newPage'); // throws `\\yii2tech\\content\\ItemNotFoundException` exception\n```\n\nThis can also be performed using [[\\yii2tech\\content\\Item]]. For example:\n\n```php\nuse yii2tech\\content\\Item;\n\n/* @var $contentManager yii2tech\\content\\Manager */\n$contentManager = Yii::$app-\u003eget('pageContentManager');\n\n$contentItem = new Item([\n    'manager' =\u003e $contentManager,\n]);\n$contentItem-\u003eid = 'newPage';\n$contentItem-\u003esetContents([\n    'title' =\u003e 'New page',\n    'body' =\u003e 'New page body',\n]);\n$contentItem-\u003esave();\n```\n\n\n## Creating content management web interface \u003cspan id=\"creating-content-management-web-interface\"\u003e\u003c/span\u003e\n\nClass [[\\yii2tech\\content\\Item]] is a descendant of [[\\yii\\base\\Model]], which uses its content parts as the\nmodel attributes. Thus instance of [[\\yii2tech\\content\\Item]] can be used for creating a web forms, populating\nfrom request data and saving. Thus controller, which performs content override may look like following:\n\n```php\nuse yii\\web\\Controller;\nuse yii\\web\\NotFoundHttpException;\nuse yii2tech\\content\\ItemNotFoundException;\nuse Yii;\n\nclass PageController extends Controller\n{\n    /**\n     * Updates particular content item, creating/updating an override.\n     */\n    public function actionUpdate($id)\n    {\n        $model = $this-\u003efindModel($id);\n\n        if ($model-\u003eload(Yii::$app-\u003erequest-\u003epost())) {\n            if ($model-\u003esave()) {\n                return $this-\u003eredirect(['index']);\n            }\n        }\n\n        return $this-\u003erender('update', [\n            'model' =\u003e $model,\n        ]);\n    }\n\n    /**\n     * Restores default values for particular content item, removing an override.\n     */\n    public function actionDefault($id)\n    {\n        $model = $this-\u003efindModel($id);\n        $model-\u003ereset();\n        return $this-\u003eredirect(['index']);\n    }\n\n    /**\n     * @param string $id\n     * @return \\yii2tech\\content\\Item\n     */\n    protected function findModel($id)\n    {\n        /* @var $contentManager \\yii2tech\\content\\Manager */\n        $contentManager = Yii::$app-\u003eget('pageContentManager');\n        try {\n            $model = $contentManager-\u003eget($id);\n        } catch (ItemNotFoundException $e) {\n            throw new NotFoundHttpException('Requested page does not exist.');\n        }\n        return $model;\n    }\n\n    // ...\n}\n```\n\nThe view file for 'update' action may look like following:\n\n```php\n\u003c?php\n\nuse yii\\widgets\\ActiveForm;\nuse yii\\helpers\\Html;\n\n/* @var $this yii\\web\\View */\n/* @var $model yii2tech\\content\\Item */\n\n$this-\u003etitle = Yii::t('admin', 'Update Page: ') . $model-\u003eid;\n?\u003e\n\u003cdiv class=\"row\"\u003e\n    \u003c?php $form = ActiveForm::begin(); ?\u003e\n\n    \u003c?= $form-\u003efield($model, 'title')-\u003etextInput() ?\u003e\n\n    \u003c?= $form-\u003efield($model, 'body')-\u003etextarea() ?\u003e\n\n    \u003cdiv class=\"form-group\"\u003e\n        \u003c?= Html::submitButton(Yii::t('admin', 'Save'), ['class' =\u003e 'btn btn-primary']) ?\u003e\n    \u003c/div\u003e\n\n    \u003c?php ActiveForm::end(); ?\u003e\n\u003c/div\u003e\n```\n\nYou may setup your own validation rules, attribute labels and hints for the [[\\yii2tech\\content\\Item]] model using\n[[\\yii2tech\\content\\Manager::$itemConfig]]. For example:\n\n```php\nreturn [\n    'components' =\u003e [\n        'pageContentManager' =\u003e [\n            'class' =\u003e 'yii2tech\\content\\Manager',\n            'itemConfig' =\u003e [\n                'class' =\u003e 'yii2tech\\content\\Item',\n                'rules' =\u003e [\n                    [['title', 'body'], 'required'],\n                    ['title', 'string', 'max' =\u003e 255],\n                ],\n                'labels' =\u003e function () {\n                    return [\n                        'title' =\u003e Yii::t('page', 'Title'),\n                        'body' =\u003e Yii::t('page', 'Body'),\n                    ];\n                },\n                'hints' =\u003e function () {\n                    return [\n                        'title' =\u003e Yii::t('page', 'Page title...'),\n                        'body' =\u003e Yii::t('page', 'Page body content...'),\n                    ];\n                },\n            ],\n            // ...\n        ],\n    ],\n    // ...\n];\n```\n\n\u003e Note: by default, if [[\\yii2tech\\content\\Item::$rules]] are not specified - the 'required' validator will be set\n  for all attributes.\n\nYou may also use your own class for the content item, specifying attribute validation rules, labels and hints in ordinary way.\n\nIn order to get full list of content items available at particular manager [[\\yii2tech\\content\\Manager::getAll()]] method\ncan be used. So controller action, which lists all available pages, may look like following:\n\n```php\nuse yii\\web\\Controller;\nuse Yii;\n\nclass PageController extends Controller\n{\n    /**\n     * Displays list of all available content items.\n     */\n    public function actionIndex()\n    {\n        /* @var $contentManager \\yii2tech\\content\\Manager */\n        $contentManager = Yii::$app-\u003eget('pageContentManager');\n\n        $dataProvider = new ArrayDataProvider([\n            'allModels' =\u003e $contentManager-\u003egetAll(),\n            'pagination' =\u003e false,\n        ]);\n\n        return $this-\u003erender('index', [\n            'dataProvider' =\u003e $dataProvider,\n        ]);\n    }\n\n    // ...\n}\n```\n\nThen the view for the listing may look like following:\n\n```php\n\u003c?php\n\nuse yii\\grid\\ActionColumn;\nuse yii\\grid\\GridView;\n\n/* @var $this yii\\web\\View */\n/* @var $dataProvider yii\\data\\ArrayDataProvider */\n?\u003e\n\u003c?= GridView::widget([\n    'dataProvider' =\u003e $dataProvider,\n    'columns' =\u003e [\n        'id',\n        'title',\n        'body',\n        [\n            'class' =\u003e ActionColumn::className(),\n            'template' =\u003e '{update} {default}',\n            'buttons' =\u003e [\n                'default' =\u003e function ($url) {\n                    $icon = Html::tag('span', '', ['class' =\u003e 'glyphicon glyphicon-repeat']);\n                    return Html::a($icon, $url, ['title' =\u003e 'Default', 'data-confirm' =\u003e 'Are you sure you want to restore defaults for this item?']);\n                },\n            ],\n        ],\n    ]\n]); ?\u003e\n```\n\n**Heads up!** While using [[\\yii2tech\\content\\Manager::getAll()]] provides a quick simple way for content listing building,\nit is not efficient regardless to the computing resources and memory consumption. While using it, the program may reach\nPHP memory limit in case you have many content items with large data associated.\n\n\n## Working with meta data \u003cspan id=\"working-with-meta-data\"\u003e\u003c/span\u003e\n\nWorking with content templates, you may want to setup some reference information about them, for example\nprovide description for variables (placeholders) used inside the template. Such information may vary depending\non particular content set, like variables for 'about' page may be different from the ones for 'how-it-works' page.\nThus it is good practice to save such meta information along with the default content, e.g. in the source file.\nFor example:\n\n```php\n\u003c?php\n// file '@app/data/pages/about.php'\nreturn [\n    'title' =\u003e 'About {{appName}}',\n    'body' =\u003e 'About page content',\n    'pageUrl' =\u003e ['/site/about'],\n    'placeholderHints' =\u003e [\n        '{{appName}}' =\u003e 'Application name'\n    ],\n];\n```\n\nYou can declare meta content parts list using [[\\yii2tech\\content\\Manager::$metaDataContentParts]], for example:\n\n```php\nreturn [\n    'components' =\u003e [\n        'pageContentManager' =\u003e [\n            'class' =\u003e 'yii2tech\\content\\Manager',\n            'metaDataContentParts' =\u003e [\n                'pageUrl',\n                'placeholderHints',\n            ],\n            // ...\n        ],\n    ],\n    // ...\n];\n```\n\nContent parts listed at [[\\yii2tech\\content\\Manager::$metaDataContentParts]] will not be returned by\n[[\\yii2tech\\content\\Manager::get()]] or [[\\yii2tech\\content\\Manager::getAll()]] methods and thus will not\nbe populated inside [[\\yii2tech\\content\\Item::$contents]]. You should use [[\\yii2tech\\content\\Manager::getMetaData()]]\nor [[\\yii2tech\\content\\Item::getMetaData()]] to retrieve them. For example:\n\n```php\n/* @var $contentManager yii2tech\\content\\Manager */\n$contentManager = Yii::$app-\u003eget('pageContentManager');\n\n$contentItem = $contentManager-\u003eget('about');\nvar_dump($contentItem-\u003ehas('pageUrl')); // outputs `false`\n\n$metaData = $contentManager-\u003egetMetaData('about');\nvar_dump($metaData['pageUrl']);\n\n$metaData = $contentItem-\u003egetMetaData();\nvar_dump($metaData['pageUrl']);\n```\n\nMeta data usage helps in composition of user-friendly content management interface.\n\n\n## Email template management \u003cspan id=\"email-template-management\"\u003e\u003c/span\u003e\n\nDo not limit yourself with 'pages' example. This extension may be used for several purposes, including email template\nmanagement. Application configuration example:\n\n```php\nreturn [\n    'components' =\u003e [\n        'mailContentManager' =\u003e [\n            'class' =\u003e 'yii2tech\\content\\Manager',\n            'sourceStorage' =\u003e [\n                'class' =\u003e 'yii2tech\\content\\PhpStorage',\n                'filePath' =\u003e '@app/data/mail',\n            ],\n            'overrideStorage' =\u003e [\n                'class' =\u003e 'yii2tech\\content\\DbStorage',\n                'table' =\u003e '{{%MailTemplate}}',\n                'contentAttributes' =\u003e [\n                    'subject',\n                    'body',\n                ],\n            ],\n        ],\n    ],\n    // ...\n];\n```\n\nEmail message composition example:\n\n```php\n/* @var $contentManager yii2tech\\content\\Manager */\n$contentManager = Yii::$app-\u003eget('mailContentManager');\n$contentItem = $contentManager-\u003eget('contact');\n\nYii::$app-\u003emailer-\u003ecompose()\n    -\u003esetTo($admin-\u003eemail)\n    -\u003esetFrom([Yii::$app-\u003eparams['appEmail'] =\u003e Yii::$app-\u003ename])\n    -\u003esetSubject($contentItem-\u003erender('subject', ['appName' =\u003e Yii::$app-\u003ename, 'form' =\u003e $this]))\n    -\u003esetHtmlBody($contentItem-\u003erender('body', ['appName' =\u003e Yii::$app-\u003ename, 'form' =\u003e $this]))\n    -\u003esend();\n```\n\nYou may simplify this task using [[\\yii2tech\\content\\mail\\MailerContentBehavior]] behavior, which, being attached to the mailer\ncomponent, provides a shortcut method `composeFromContent()` for quick mail message composition from the content item.\nApplication configuration example:\n\n```php\nreturn [\n    'components' =\u003e [\n        'mailContentManager' =\u003e [\n            // ...\n        ],\n        'mailer' =\u003e [\n            'class' =\u003e 'yii\\swiftmailer\\Mailer',\n            'as content' =\u003e [\n                'class' =\u003e 'yii2tech\\content\\mail\\MailerContentBehavior',\n                'contentManager' =\u003e 'mailContentManager',\n                'messagePopulationMap' =\u003e [\n                    'subject' =\u003e 'setSubject()',\n                    'body' =\u003e 'setHtmlBody()',\n                ],\n            ],\n        ],\n    ],\n    // ...\n];\n```\n\nEmail message composition example:\n\n```php\nYii::$app-\u003emailer-\u003ecomposeFromContent('contact', ['appName' =\u003e Yii::$app-\u003ename, 'form' =\u003e $this])\n    -\u003esetTo($admin-\u003eemail)\n    -\u003esetFrom([Yii::$app-\u003eparams['appEmail'] =\u003e Yii::$app-\u003ename])\n    -\u003esend();\n```\n\n\n## Internationalization \u003cspan id=\"internationalization\"\u003e\u003c/span\u003e\n\nIn case you have a multi-lingual project and its content should vary depending on chosen interface language. The best way\nto handle it will be usage of composite content IDs. Such ID should include actual language as its part. For example:\ninstead of using 'about', you should operate 'en/about', 'ru/about' and so on. File-based storages like [[\\yii2tech\\content\\PhpStorage]]\nand [[\\yii2tech\\content\\JsonStorage]] are able to operate sub-folders. So the source files structure will look like following:\n\n```\ndata/\n    pages/\n        en/\n            about.php\n            how-it-works.php\n        ru/\n            about.php\n            how-it-works.php\n    mail/\n        en/\n            contact.php\n            reset-password.php\n        ru/\n            contact.php\n            reset-password.php\n```\n\nWhile retrieving the particular content item, its ID should be composed using [[\\yii\\base\\Application::$language]].\nFor example:\n\n```php\n/* @var $contentManager yii2tech\\content\\Manager */\n$contentManager = Yii::$app-\u003eget('pageContentManager');\n$contentItem = $contentManager-\u003eget(Yii::$app-\u003elanguage . '/about');\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyii2tech%2Fcontent","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyii2tech%2Fcontent","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyii2tech%2Fcontent/lists"}