⬆️ ⬇️

Use Yii2. We write another CMS or an attempt to significantly speed up development with minimal overhead

Practically every developer who has been working with a specific framework for a long time has a set of his own tools that allow him to speed up the development of the site. However, often, they are poorly structured, have no documentation and are being finalized in the “process” of production.



The idea of ​​the yicms project was to collect all the developments for the Yii2 framework, which seemed convenient to a certain system, at the head of which should be ease of use, flexibility and the ability to significantly accelerate the development of standard sites using its tools. This project was developed by me for the "soul", but at the moment it is already in beta.



The essence of yicms lies somewhere in the middle between the capabilities of the framework and the usual features of the CMS. This is not a CMS in the usual sense of the word, but simply a set of modules that can significantly accelerate the development of the site using its tools, while having full "access" to the capabilities of the framework. The main features that are incorporated in yicms are the admin-generated on-the-fly and auto-annotated classes that allow you to use IDE auto-completion and have an intuitive interface.



Installation and Setup



So, let's get acquainted with yicms with its installation. For dating it is easiest to use the Open Server or any other similar server platform. Since yicms has a hard dependency on Yii, we will first install it. For simplicity, we will use the basic pattern:

')

composer create-project --prefer-dist yiisoft/yii2-app-basic basic



After the composer installs the framework, add the section require of the composer.json file with four modules yicms and run composer update.



  "require": { "php": ">=5.4.0", "yiisoft/yii2": "~2.0.14", "yiisoft/yii2-bootstrap": "~2.0.0", "yiisoft/yii2-swiftmailer": "~2.0.0 || ~2.1.0", "iliich246/yii2-yicms-common": "dev-master", "iliich246/yii2-yicms-pages": "dev-master", "iliich246/yii2-yicms-essences": "dev-master", "iliich246/yii2-yicms-feedback": "dev-master" }, 


Each module represents a set of specific functionality, which will be considered later. For now, just install them all.



Next, you need to remember to configure the server. In the Open Server we use Apache and configure .htaccess as follows:



For the root .htaccess file:

 Options +FollowSymLinks IndexIgnore */* RewriteEngine on RewriteCond %{REQUEST_URI} !^/(web) RewriteRule ^assets/(.*)$ /web/assets/$1 [L] RewriteRule ^css/(.*)$ web/css/$1 [L] RewriteRule ^js/(.*)$ web/js/$1 [L] RewriteRule ^images/(.*)$ web/images/$1 [L] RewriteRule ^files/(.*)$ web/files/$1 [L] RewriteRule (.*) /web/$1 RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . web/index.php 


For the .htaccess file in the web directory



 RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . index.php 


The next step is to roll the migrations of each yicms module into the database. In the console, execute the following commands in sequence:



yii migrate/up --migrationPath=@vendor/iliich246/yii2-yicms-common/Migrations



yii migrate/up --migrationPath=@vendor/iliich246/yii2-yicms-pages/Migrations



yii migrate/up --migrationPath=@vendor/iliich246/yii2-yicms-essences/Migrations



yii migrate/up --migrationPath=@vendor/iliich246/yii2-yicms-feedback/Migrations



Now we will start configuration in the config / web.php file.



We configure the modules section as follows:



 'modules' => [ 'common' => [ 'class' => 'Iliich246\YicmsCommon\CommonModule', ], 'pages' => [ 'class' => 'Iliich246\YicmsPages\PagesModule', ], 'essences' => [ 'class' => 'Iliich246\YicmsEssences\EssencesModule', ], 'feedback' => [ 'class' => 'Iliich246\YicmsFeedback\FeedbackModule', ], 'redactor' => 'yii\redactor\RedactorModule', ], 


The bootstrap section is configured as follows:



 'bootstrap' => ['log', 'common', 'pages', 'essences', 'feedback'], 


Set up the user section as follows:



 'user' => [ 'identityClass' => 'Iliich246\YicmsCommon\Base\CommonUser', 'enableAutoLogin' => true, ], 


Remove commenting section urlManager:



 'urlManager' => [ 'enablePrettyUrl' => true, 'showScriptName' => false, 'rules' => [ ], ], 


The Yii2 framework and the yicms system are successfully configured.



After the first launch, if everything has been configured correctly, the yicms directory should be created in the root directory of the project, which will contain the files that can be modified by the user and also create auto-annotated classes in it.







Description of Common and Pages Modules



The yicms system consists of two sections. Dev section, which should be available only to the developer (it can break the code in it) and the section Admin should be accessible to the site administrator (it manages the content). To get started, go to the Dev section, for this you need to use the following URL:



http://< >/web/common/dev/login-as-dev?hash=123456&asDev



After this, the Dev panel should open:







Now we start to understand the functionality of yicms. To begin with, in the Pages module we will create a page called index, which will be the main page of the site. Go to the List of pages and create a new page called index. By default, the title, meta_description and meta_keywords fields will be created for the page. Additionally, we will create another field through the add new field button with the name text, all of its parameters will remain the default.



Now if you follow the link



http://< >/web/pages/admin



The admin section will open. The index page will be available in it for filling in with content. Fill in all fields with arbitrary text.



At the time of creation in the dev section of the index page, the Index class was created in the yicms / Pages / Models directory in which annotations were also created for the title, meta_description, meta_keywords fields, and also for the text field that we created on the fly. We can use the object of this class to get the field values. Consider how this can be done. In the standard controller SiteController of the basic template in action actionIndex we will write the following:



 /** * Displays homepage. * * @return string * @throws \Iliich246\YicmsPages\Base\PagesException */ public function actionIndex() { $indexPage = \app\yicms\Pages\Models\Index::getInstance(); return $this->render('index', [ 'indexPage' => $ indexPage ]); } 


Since the \ app \ yicms \ Pages \ Models \ Index class was generated automatically, the auto-completion for it will work in the IDE. Now $ indexPage can be used as an index. Now open the index view. Remove from there all unnecessary and write:



 <?php /** @var $this yii\web\View */ /** @var $indexPage \app\yicms\Pages\Models\Index */ $this->title = $indexPage->title; $this->registerMetaTag([ 'name' => 'description', 'content' => $indexPage->meta_description ]); $this->registerMetaTag([ 'name' => 'keywords', 'content' => $indexPage->meta_keywords ]); ?> <div class="site-index"> <div class="jumbotron"> <h1><?= $indexPage->text ?></h1> </div> <div class="body-content"> </div> </div> 


Annotation of the $ indexPage variable is required for IDE autocompletion. Now use the $ indexPage object. All the fields that we created in the dev section for the index page are available in the Index class object, which, thanks to autoannotation, are supplemented by the IDE after entering the arrow operator.



In total, in a short time we created an Index page for which an admin panel was automatically created to edit its content. We used programming only to create a page object in a standard framework controller, and in a form in which we could quickly use the page object thanks to auto-completion and an intuitive interface.



We considered only the minimum part of the yicms capabilities. Now we will slightly complicate the main page of the site using yicms.



yicms supports multilingual out of the box. To activate the second language in the dev section of the common module, select the list of languages, then select the Russian language and activate it in it:







Now in the admin panel we will be able to fill in the fields for several languages. You can create new languages ​​in the system. However, it will be necessary to translate the admin panel using the standard framework tools. Therefore, when creating a new language, you must adhere to ISO. For content controls just add a new plate with a new language.



Component file blocks



Next, consider the element file blocks. In the dev panel of the index page we will create a file block with the program name document. The remaining block settings will remain the default. Now in the admin panel for the index page, editing of the document file has become available. Load an arbitrary file. Now the link to download it can be displayed on the index page of the site:



 … <div class="body-content"> <div class="row"> <h3>Document</h3> <a href="<?= $indexPage->document->uploadUrl() ?>"> <?= $indexPage->document ?> </a> </div> … </div> 


Now on the link page, the file that we uploaded to the admin panel will be uploaded. By default, the file block is in single-file mode for all languages. However, in the dev panel you can switch the Language type to the Translateable type and then for each language you will need to load your own file. In this case, the link will be downloaded to the file whose language is currently active on the site. Of course, you can download for a specific language, which will be considered later.



The next step in the dev panel is to create a file recipes for which we specify the type as multiple type. In this mode, you can upload many files for a single file block. To do this, in the admin panel, in the appeared recipes tab, we will upload several arbitrary files.

After that in the form of index we display the list of files:



 … <div class="row"> <h3>Recipes</h3> <?php foreach ($indexPage->recipes as $recipe): ?> <a href="<?= $recipe->uploadUrl() ?>"> <?= $recipe ?> </a> <?php endforeach; ?> </div> … 


In the multiple type mode, the file block can be bypassed via foreach or an iterator.



Component images block



Now consider the images block element. Let's create a picture block for the index page, leaving all the default parameters. Similarly, the file blocks in the admin panel, the opportunity to upload a picture picture. Load an arbitrary image. Now in the form of index we will write the following:



 <div class="row"> <h3>Image</h3> <img src="<?= $indexPage->picture ?>" alt=""> </div> 


Similar to image files, by default one is loaded into all languages, however, for images, this behavior can be changed in the dev panel and you can upload your own image for each language. You can also upload many images into one block.



Create a gallery image block in the dev panel, set its type as Multiple images. For images, you can also hang text boxes on the fly. Therefore, in the modal window of the gallery block settings, go to the section “View image block field”. In this section, for images, create fields named alt and name. Now in the admin panel for each image there is an opportunity to set text fields alt and name. Also in yicms for the images out of the box the possibility of “cropping” and creating thumbnails is available. For example, create a thumbnail template for the gallery block. In the modal window of the image block, go to the “Config images thumbnails” section. In it, we will create a thumbnail configurator with the name “x2” and the parameters divider = 2 and quality = 80. By name we will be able to refer to the thumbnail, the divider will determine how much to reduce the size of the original image, quality determines the quality of the thumbnail compression. Next, we will create another miniature template with parameters: program_name = "x5", divider = 5, quality = 50. Now, when we load the image from the admin panel, two miniatures with the parameters we have chosen will be automatically created for it.



Now we will upload several images into the admin panel for the gallery group. After the image is created and uploaded, you can fill in the alt and name fields, the templates of which we created in the dev panel. Let's see how to display all this on the site.



 <div class="row"> <?php foreach($indexPage->gallery->getImages() as $image): ?> <div class="col-lg-4"> <img src="<?= $image ?>" width="100%" alt="<?= $image->alt ?>"> <img src="<?= $image->outputThumbnail('x2') ?>" alt="<?= $image->alt ?>"> <img src="<?= $image->outputThumbnail('x5') ?>" alt="<?= $image->alt ?>"> <p><?= $image->name ?></p> </div> <?php endforeach; ?> </div> 


Here, the getImages () method returns an array of objects of the autoannotated class app \ yicms \ Pages \ Models \ Index \ Images \ GalleryImage in which annotations were created for the alt and name fields that we created on the fly and IDE autocompletion is available for them. Also, by calling the outputThumbnail method (<Name of the thumbnail template>) on the object, we switch the object to the thumbnail output mode.



For image blocks it is possible to create not only fields but also conditions (conditions) that will be considered by us a little later. You can also create fields and conditions for file blocks. Works on a similar principle.



Conditions component



Now consider the next item. These are conditions. Conditions are convenient for creating lists, checkboxes and so on in the admin panel. Which will determine certain behaviors when pages are operated. Consider an example. Create a condition in the dev panel on the index page with the name background. Type select “select dropdown type”. After creating the conditions go to the section "Config condition options". Create three condition values ​​with transparent, red, and green values.



After that, a condition appears in the admin panel of the index page with a drop-down list with the values ​​TRANSPARENT, RED and GREEN. They are translated to upper case, since in the newly created auto-annotated class they are represented as constants.



Now we will try to use the created condition. We use it in a slightly different way to show other possibilities. Since the index page in the site is home, it will semantically correctly change the background color of the site from its settings. Therefore, our condition will have to change the background color throughout the site, so we insert it into the layout of the controller. Open the layout template Yii2 framework template file and create an Index object:



 <?php /* @var $this \yii\web\View */ /* @var $content string */ use app\widgets\Alert; use yii\helpers\Html; use yii\bootstrap\Nav; use yii\bootstrap\NavBar; use yii\widgets\Breadcrumbs; use app\assets\AppAsset; AppAsset::register($this); $pagesIndex = \app\yicms\Pages\Models\Index::getInstance(); ?> <?php $this->beginPage() ?> <!DOCTYPE html> <html lang="<?= Yii::$app->language ?>"> <head> <meta charset="<?= Yii::$app->charset ?>"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <?php $this->registerCsrfMetaTags() ?> <title><?= Html::encode($this->title) ?></title> <?php $this->head() ?> </head> <body> <?php $this->beginBody() ?> 


Since the object of the Index class is something like a singleton, we can call the static getInstance () method as much as you like without the risk of making unnecessary duplicate queries to the database and other overhead costs when it is created. Now, using the $ pagesIndex object, we change the tag by adding an inline style to it:



 <body style="background-color: <?php if ($pagesIndex->background->value() == \app\yicms\Pages\Models\Index\Conditions\Background::TRANSPARENT):?> transparent; <?php elseif ($pagesIndex->background->value() == \app\yicms\Pages\Models\Index\Conditions\Background::RED):?> #FFD3D3 <?php else: ?> #BBFFC8 <?php endif; ?> "> 


Calling $ pagesIndex-> ​​background-> value () and comparing with the constant that was created in the auto-annotated class \ app \ yicms \ Pages \ Models \ Index \ Conditions \ Background we choose the background color of the site.



Next, we consider the possibilities of autoannotated classes. During yicms, a lot of events occur that can be processed by the Yii framework. Let's give an example.



 app\yicms\Pages\Models\Index\Fields\Text 


This file was created by the auto-annotator while we were creating the text field for the index page in the dev panel. When we write <? = $ IndexPage-> text?> In the template, we create something similar to the singleton of this particular class, which in the template is cast to the string through the magic __toString ().



To change the content, subscribe to the event and process it via the callback function:



 class Text extends Field { public function init() { $this->on(self::EVENT_BEFORE_OUTPUT, function($event) { /** @var $event \Iliich246\YicmsCommon\Base\HookEvent */ }); } } 


The EVENT_BEFORE_OUTPUT event occurs before displaying the contents of a field. Let's try to change it. To do this, use the event object \ Iliich246 \ YicmsCommon \ Base \ HookEvent:



 $this->on(self::EVENT_BEFORE_OUTPUT, function($event) { /** @var $event \Iliich246\YicmsCommon\Base\HookEvent */ $value = $event->getHook(); $value = strrev($value); $event->setHook($value); }); 


The HookEvent event gets the value of the field before the event fires. In an event, you can get this value through $ event-> getHook (), change it, and send it back to the object through the event using $ event-> setHook ($ value). Thus in the event we turned the text backwards, which displays the Text field.



Virtually every autoannounced class has a number of events to which you can connect in this way, but their consideration goes beyond the introductory article.



Total in this article we considered two modules yicms, namely the modules Common and Pages. The Common module includes components such as Fields, Files Blocks, Images Blocks and Conditions. The Pages module is responsible for creating page objects in which all elements of the Common module are present. Those. all yicms modules depend on the main Common module.



Yii2-yicms-essences module



Now we will get acquainted with the Essences module. This module is designed to create entities such as category-> entity for example, product category-> product, vehicle type (cars, motorcycle) -> specific vehicle, and so on. Within the module, these elements are called categories (categories) and representations (represents).



For example, we will show the creation of the simplest category-> type catalog for an online store.



In the Dev section of the Essences module, we’ll go to the list of essences and go to the create new essence section. Give it the program name of products, and leave the rest of the settings by default. By default, an entity with a behavior is created: one view can belong to one category. However, you can specify other behaviors: just creating views without categories, or a single view can belong to several categories.



The categories themselves are built according to the tree principle, when any category has a parent (except the highest one). Those. categories are leaves of a tree type data structure.



Total product essence is created. For her, auto-annotated classes were created in the app \ yicms \ Essences \ Models directory. Next in the dev panel we need to create class templates and presentation templates. All this is similar to how we created them for the index page. For them, the Fields, Files Blocks, Images Blocks and Conditions elements are available on the fly. As soon as we are done with the creation of templates in the admin panel, the creation of categories and views with the elements of the templates that we set in the dev panel is available.



So, we will create several categories of goods. And for each category of goods several products. Once this is done the whole thing can be displayed on the main site. Consider how this is done. In SiteController, create an actionProcuts action:



 /** * * @return string */ public function actionProducts() { $productsEssence = Products::getInstance(); return $this->render('products', [ 'productsEssence' => $productsEssence ]); } 


For it, create the products view.



 <?php /** @var $this yii\web\View */ /** @var $productsEssence \app\yicms\Essences\Models\Products */ ?> <div class="body-content"> <?php foreach($productsEssence->categories as $category): ?> <h1>: <?= $category->name ?></h1> <?php foreach($category->represents as $product): ?> <h2>: <?= $product->name ?></h2> <?php endforeach; ?> <?php endforeach; ?> </div> 


Thanks to the autoannotation mechanism, the $ productsEssence-> categories modode returns an array of objects of the auto-annotated class app \ yicms \ Essences \ Models \ ProductsCategory. As in the previous autoannotated classes, there are annotations on the fields in this class, which we created on the fly, the IDE can use auto-completion. It is also worth noting that the autoannotated classes were developed with the idea that the user yicms would be able to place their own business logic in them, i.e. you can write your own code in them, and the auto-annotation mechanism changes specially marked areas and no user code will be affected.



The $ category-> represents method also returns an array of app \ yicms \ Essences \ Models \ ProductsRepresents objects that have all the same autoannotation advantages as for categories. Of course, there are more flexible methods for obtaining representations. For example, $ category-> getRepresentsQuery () will return the ActiveQuery object of the Yii2 framework, which can be further configured to refine the query to the database, use to create pagination or lazy loading.



Total module Essenses allows you to very quickly create the skeleton of the functional store, with the admin working already. Additional business logic can be written in autoannotated classes.



Yii2-yicms feedback module



This module is designed to construct on the website information entry forms by guests and site users. The ideology of the work remains the same; first, we type the required fields in the dev panel.



Consider the work of the module by example. Let's create an entry with the program name messages in the dev panel in the feedback module. On the messages settings page we can see the button Feedback pages templates. Using this link we can create elements of the type field, files block, images block, conditions that can be used to decorate the content of the messages page. They are no different from what we considered for the Pages module. Now we are more interested in the section Feedback input templates. In this section, we will be able to create such entities as input fields, input files, input images and input conditions. These entities are just necessary for creating user input forms.



Create two input fields with the names name and surname. Input image with the name photo. Input condition named agreement.For now, let's leave all the default settings for these entities. Later in this example, we will configure validators that are also attached to these entities on the fly (Validators are also available for Common entity entities and work according to the same logic as for the feedback module. We will look at it a little later).



Now apply the module in our website. First, create a new action in SiteController:



 /** * @return string */ public function actionMessage() { $messages = \app\yicms\Feedback\Models\Messages::getInstance(); return $this->render('message', [ 'messages' => $messages ]); } 


Next, create a message view and write the following in it:



 <?php /** @var $this yii\web\View */ /** @var $message \app\yicms\Feedback\Models\Messages */ ?> <?php $form = \yii\bootstrap\ActiveForm::begin([ 'id' => 'some-form', 'options' => [ 'data-pjax' => true, ], ]); ?> <div class="row"> <div class="col-xs-12"> <?= \yii\helpers\Html::submitButton('Save', ['class' => 'btn btn-success']) ?> </div> </div> <?php \yii\bootstrap\ActiveForm::end(); ?> 


To create the form, we will use the standard tools of the framework. In order to take advantage of the feedback module, we will write the following:



 <?php if ($messages->input_name->isActive): ?> <?= $form->field($messages->input_name, $messages->input_name->key) ?> <?php endif; ?> <?php if ($messages->input_surname->isActive): ?> <?= $form->field($messages->input_surname, $messages->input_surname->key) ?> <?php endif; ?> <?php if ($messages->input_photo->isActive): ?> <?= $form->field($messages->input_photo, $messages->input_photo->key) ->fileInput() ?> <?php endif; ?> <?php if ($messages->input_agreement->isActive): ?> <?= $form->field($messages->input_agreement, $messages->input_agreement->key)->checkbox() ?> <?php endif; ?> 


For entities of type input, you must use the prefix input_, otherwise autoannotations work for them the same way as for other things in yicms.



At the beginning there is a check on the field activity, if the field is inactive, then it should not be rendered, and even if it is rendered, the data that was filled in it will not be uploaded to the server. The field activity can be enabled / disabled in the dev panel.



Next comes the standard form of the framework where you need to write like this.



 $form->field($messages->input_name, $messages->input_name->key) 


$ messages-> input_name returns an object of class app \ yicms \ Feedback \ Models \ Message \ InputFields \ Name which is a successor to the Model framework Yii2 and can be used in forms, $ messages-> input_name-> key returns the correct key value, in order so that the form can be loaded.



Now, when we go to the page with the form, all its fields are rendered, but the upload to the server is not working yet. Make it very easy. I tried to make the interface of working with feedback as similar as possible to the interface of working models in the framework. Yes, I had to “sacrifice” a bit of architecture, but in general it turned out like this:



 public function actionMessage() { $messages = \app\yicms\Feedback\Models\Messages::getInstance(); if ($messages->load(Yii::$app->request->post()) && $messages->validate()) { $messages->handle(); } return $this->render('message', [ 'messages' => $messages ]); } 


Now the form is loaded, validated and saved on the server. In the admin section, we can see the information that was entered into the form.



Of course, you can use the auto-annotated class:



\app\yicms\Feedback\Models\Messages



 class Messages extends Feedback { /** * @return self instance . */ public static function getInstance() { return self::getByName('messages'); } /** * @inheritdoc */ public function init() { $this->on(self::EVENT_AFTER_HANDLE, function($event) { Yii::$app->mailer->compose('message', [ 'message' => $this ]) ->setFrom('from@domain.com') ->setTo('to@domain.com') ->setSubject('Message subject') ->send(); }); } } 


In this example, we have subscribed to the event EVENT_AFTER_HANDLE. The event occurs after the form data has been saved to the server. In this case, we send an email, in the template of which we can insert saved form data:



 <?php /** @var $this \yii\web\View view component instance */ /** @var $message \app\yicms\Feedback\Models\Message */ ?> <h1>Mail on message</h1> <h1>Name: <?= $message->input_name->value ?></h1> <h2>Surname: <?= $message->input_surname->value ?></h2> 


$ message-> input_name-> value simply returns the current data that has been saved to the database.



Now let's see how validators work. Add the Required validator to the input field entity named name in the dev panel. This is the standard validator yii \ validators \ RequiredValidator framework. When we added it, it is not active yet, since the button with its name is white. In order to activate it, you must click on this button, the validator settings window will open. There you need to install the checkbox and activate it. At the same time there you can configure all other parameters of the validator, such as error messages in all languages ​​of the system, etc. After saving, the validator icon turns green, which means it has become active and will be applied when validating forms.



On the input_condition with the name agreement, we hang the compare validator. And we set it up for comparison with 1, the number type and the == operator. Additionally, you can write a message in the error text fields, such as "You must necessarily agree to the terms of the offer." Now the validation on the form will take place only in the case when the agreement checkbox is selected.



Total in this article we reviewed the main features of the system yicms.



PS The work on yicms was very interesting and exciting for me, and personally the system seems to me quite comfortable. However, this is my subjective opinion. I am wondering if it will be useful for the community, should I tidy up the repositories, write documentation, cover the system with tests, etc.

Source: https://habr.com/ru/post/454914/



All Articles