Сервис на Yii2: Настройки IDE и модули

Продолжаем разработку нашего чудо-сервиса на Yii2. На прошлом уроке мы создали через Composer новый проект и дополнили его раздельной системой конфигурационных файлов. Сегодня мы внедрим в проект модульную структуру и немного лучше познакомимся с базовыми настройками и некоторыми возможностями авторефакторинга в PhpStorm IDE. Другими IDE я не пользуюсь, поэтому с ними не могу вам помочь.

Репозиторий проекта на GitHub

Часть 1: Установка и настройка приложения

Предварительная настройка IDE

При работе с использованием IDE нужно учитывать и её нюансы.

Во-первых, у нас в проекте есть временные мусорные папки runtime и web/assets. Их желательно исключить из зоны видимости любой IDE, чтобы они попусту не захламляли её внутреннюю историю изменений, не участвовали в поиске, и чтобы список файлов внутри них не занимал оперативную память.

Во-вторых, у нас есть внушительных размеров папка vendor. Включение её в проект полезно, так как это позволит работать автоподстановке, сканирующей все константы, классы и методы.

Но в IDE кроме сканера автоподстановки имеется множество других инструментов для обхода файлов проекта. Это может быть банальный поиск по содержимому, рефакторинг, поиск ошибок с помощью инспектора CodeInspect Code или вывод списка TODO из всех файлов. Этим инструментам нежелательно сканировать папку vendor, чтобы, например, не выводить сотни найденных TODO и стилистических ошибок из тех же встроенных минимизированных сркриптов.

Соответственно, в PhpStorm IDE войдем в настройки нашего проекта FileSettings, выделим каждую «лишнюю» папку и исключим её, пометив как Excluded:

PhpStorm Yii2 Excludes

Также нам нужно пометить корневую папку как Source Root и папку tests как Tests. При этом щёлкнув на значок P этим папкам укажем неймспейсы app и tests, чтобы PhpStorm проставлял правильный namespace при создании классов.

Мы исключили и папку vendor из проекта, поэтому все классы фреймворка потеряются. Нам нужно вернуть её, но при этом не добавлять как папку проекта, а подключить как одну из системных библиотек в Settings - Lnaguages and Frameworks - PHP - Include Path. Это можно сделать вручную, нажав там + и выбрав папку vendor, а можно и воспользоваться умением IDE парсить зависимости:

При открытии проекта PhpStorm спросит нас, хотим ли мы испортировать пути автозагрузки из composer.json. Если не спросил, то мы можем настроить импорт путей вручную:

PhpStorm PHP Libraries

При этом нужно настроить PHP-интерпретатор и путь до глобального файла composer. Если всё прошло успешно, то после сохранения настроек в список Include Path секции PHP будут автоматически импортированы пути всех библиотек из vendor:

Теперь сканер автоподстановки благополучно найдёт все файлы фреймворка, классы будут также находиться через Ctrl+N, но поиск по содержимому будет проводиться только по вашим файлам. Если же нужно будет произвести поиск внутри папки vendor, то просто специально станьте на неё мышкой и нажмите Ctrl+Shift+F для поиска конкретно в ней.

Теперь не будет проблем с заменой какой-либо строки посредством Ctrl+Shift+R, так как автозамена больше не затронет файлы фреймворка.

Продолжим работу с нашим проектом.

Переход к модульной структуре

Хранить все контроллеры и другие классы в одних и тех же папках может быть удобно, но приятнее разделять функциональность на модули.

Сейчас у нас имеется всего один контроллер и три модели:

assets/
commands/
config/
controllers/
    SiteController.php
mail/
models/
    ContactForm.php
    LoginForm.php
    User.php
tests/
vendor/
views/
    layouts/
        main.php
    site/
        about.php
        contact.php
        error.php
        index.php
        login.php
web/

Один контроллер и выводит страницы, и авторизует пользователя, и обеспечивает обратную связь. Исходя из принципа единой ответственности (обязанности) такое смешение не очень оправдано и удобно.

Первым делом откроем web/index.php и проверяем, что там установлено окружение dev:

defined('YII_ENV') or define('YII_ENV', 'dev');

Вспомним, что в config/web.php у нас находится фрагмент:

if (YII_ENV_DEV) {
    // configuration adjustments for 'dev' environment
    $config['bootstrap'][] = 'debug';
    $config['modules']['debug'] = 'yii\debug\Module';
 
    $config['bootstrap'][] = 'gii';
    $config['modules']['gii'] = 'yii\gii\Module';
}

Он определяет, что в окружении разработки (development) к приложению подключаются модули debug (выводящий панель отладки и её внутренние страницы) и gii (генератор моделей, модулей и прочего), Вот именно Gii нам и пригодится.

В корневой папке проекта создаём директорию modules. Теперь открываем генератор по адресу http://localhost/gii, переходим в «Module Generator» и вводим данные для нового модуля:

Gii Module Generator

После нажатия Preview и Generate мы получим соответствующую папку:

modules/
    main/
        controllers/
            DefaultController.php
        views/
            default/
                index.php
        Module.php

Также генератор нам подскажет, что в конфигурационный файл (в нашем случае в config/web.php) нужно подключить наш сгенерированный класс:

$config = [
    'id' => 'app',
    'modules' => [
        'main' => [
            'class' => 'app\modules\main\Module',
        ],
    ],
    ...
];

Аналогично нам нужно сгенерировать и подключить заготовку модуля user. В итоге у нас будет добавлена секция modules с двумя модулями:

$config = [
    'id' => 'app',
    'modules' => [
        'main' => [
            'class' => 'app\modules\main\Module',
        ],
        'user' => [
            'class' => 'app\modules\user\Module',
        ],
    ],
    'components' => [
        ...
    ],
];

Gii помог нам сгенерировать заготовки. А теперь аккуратная ручная работа. Нужно раскидать действия из SiteController.php, существующие классы и представления по этим модулям.

Сначала переносим модели. Создаём папки modules/main/models и modules/user/models. Если у вас тоже есть хорошая IDE, то самое время задействовать её возможности по автоматическому рефакторингу.

Открываем класс LoginForm.php и щелчком по имени класса правой кнопкой мыши (или горячей клавишей <F6>) выбираем RefactorMove. В появившемся окне изменяем пространство имён на app\modules\user\models:

Gii Module Generator

и жмём Refactor и после сканирования Do Refactor. Автоматически файл перекинется в новую папку, у класса изменится namespace и во всех местах использования автоматически поменяется строка use.

Если ваш редактор так не умеет, то нужно будет переместить классы вручную.

Аналогичным образом переносим модель формы ContactForm в модуль main и User в модуль user.

Теперь дело за контроллером SiteController. Его содержимое необходимо разнести по контроллерам всех модулей.

Контроллер обратной связи сделаем отдельным в модуле main:

namespace app\modules\main\controllers;
 
use app\modules\main\models\ContactForm;
use yii\web\Controller;
use Yii;
 
class ContactController extends Controller
{
    public function actions()
    {
        return [
            'captcha' => [
                'class' => 'yii\captcha\CaptchaAction',
                'fixedVerifyCode' => YII_ENV_TEST ? 'testme' : null,
            ],
        ];
    }
 
    public function actionIndex()
    {
        $model = new ContactForm();
        if ($model->load(Yii::$app->request->post()) && $model->contact(Yii::$app->params['adminEmail'])) {
            Yii::$app->session->setFlash('contactFormSubmitted');
 
            return $this->refresh();
        } else {
            return $this->render('index', [
                'model' => $model,
            ]);
        }
    }
}

Мы перенесли в него действия captcha и actionContact.

Обратите внимание, что actionContact мы переименовали в actionIndex и соответственно заменили имя файла представления. Теперь файл views/site/contact.php поместим как modules/main/views/contact/index.php.

Если теперь удалить SiteController и перейти по адресу http://localhost/main/contact/index, то возникнет проблема с кодом подтверждения (captcha) контактной формы. Это происходит из-за того, что адрес по умолчанию /site/captcha теперь недоступен. Нужно указать новый адрес.

В модели ContactForm находим строку подключения CaptchaValidator:

['verifyCode', 'captcha'],

и указываем новый маршрут:

['verifyCode', 'captcha', 'captchaAction' => '/main/contact/captcha'],

Аналогично в файле представления modules/main/views/contact/index.php находим конструкцию вывода виджета:

<?php
<?= $form->field($model, 'verifyCode')->widget(Captcha::className(), [
    'template' => '<div class="row"><div class="col-lg-3">{image}</div><div class="col-lg-6">{input}</div></div>',
]) ?>

и добавляем такой же параметр:

<?php
<?= $form->field($model, 'verifyCode')->widget(Captcha::className(), [
    'captchaAction' => '/main/contact/captcha',
    'template' => '<div class="row"><div class="col-lg-3">{image}</div><div class="col-lg-6">{input}</div></div>',
]) ?>

Теперь контактная форма должна нормально работать.

Контроллер по умолчанию в модуле main будет выводить страницы и ошибки:

namespace app\modules\main\controllers;
 
use yii\web\Controller;
 
class DefaultController extends Controller
{
    public function actions()
    {
        return [
            'error' => [
                'class' => 'yii\web\ErrorAction',
            ],
        ];
    }
 
    public function actionIndex()
    {
        return $this->render('index');
    }
}

К нему нужно перенести все его представления index.php и error.php из папки views/site в папку modules/main/views/default.

Мы пропустили только действие actionAbout и представление about.php, так как статические страницы нам не нужны. Если потребуется организовать раздел помощи, то его можно сделать потом.

И последний модуль user. В его контроллер мы перенесём авторизацию и представление login.php:

namespace app\modules\user\controllers;
 
use app\modules\user\models\LoginForm;
use yii\filters\AccessControl;
use yii\filters\VerbFilter;
use yii\web\Controller;
use Yii;
 
class DefaultController extends Controller
{
    public function behaviors()
    {
        return [
            'access' => [
                'class' => AccessControl::className(),
                'only' => ['logout'],
                'rules' => [
                    [
                        'actions' => ['logout'],
                        'allow' => true,
                        'roles' => ['@'],
                    ],
                ],
            ],
            'verbs' => [
                'class' => VerbFilter::className(),
                'actions' => [
                    'logout' => ['post'],
                ],
            ],
        ];
    }
 
    public function actionLogin()
    {
        if (!Yii::$app->user->isGuest) {
            return $this->goHome();
        }
 
        $model = new LoginForm();
        if ($model->load(Yii::$app->request->post()) && $model->login()) {
            return $this->goBack();
        } else {
            return $this->render('login', [
                'model' => $model,
            ]);
        }
    }
 
    public function actionLogout()
    {
        Yii::$app->user->logout();
 
        return $this->goHome();
    }
}

Теперь пустые папки controllers, models и views/site в корне можно удалить. Вместо них будет только папка modules.

В итоге получилась новая файловая структура, состоящая из двух модулей:

assets/
commands/
config/
mail/
modules/
    main/
        controllers/
            DefaultController.php
            ContactController.php
        models/
            ContactForm.php
        views/
            default/
                error.php
                index.php
            contact/
                index.php
        Module.php
    user/
        controllers/
            DefaultController.php
        models/
            LoginForm.php
            User.php
        views/
            default/
                login.php
        Module.php
tests/
vendor/
views/
    layouts/
        main.php
web/

И немного доработаем представления. Если зайдём в любое представление вроде modules/user/views/default/login.php, то увидим там вспомогательный класс, помогающий по HTML-коду страницы в браузере определить текущий маршрут:

<?php
<div class="site-login">

Это полезная подсказка для разработчика проекта. Также этот класс может быть полезен для установки персональных стилей для каждой страницы в CSS-файле. Но теперь у нас есть модули и мы перенесли это представление. Оставлять старые значения мы не будем, поэтому теперь пробежимся по всем файлам и поменяем классы на новые. Например, у этого представления теперь новый маршрут:

<?php
<div class="user-default-login">

Теперь необходимо немного доработать файлы конфигурации. Первым делом, укажем маршрут по умолчанию вместо site/index и действие для вывода ошибок вместо site/error в config/web.php. Помимо этого нужно добавить новый путь для свойства loginUrl:

$config = [
    'id' => 'app',
    'defaultRoute' => 'main/default/index',
    'components' => [
        'user' => [
            'identityClass' => 'app\modules\user\models\User',
            'enableAutoLogin' => true,
            'loginUrl' => ['user/default/login'],
        ],
        'errorHandler' => [
            'errorAction' => 'main/default/error',
        ],
        'request' => [
            'cookieValidationKey' => '',
        ],
        'log' => [
            'traceLevel' => YII_DEBUG ? 3 : 0,
        ],
    ],
];

Также дополним правила маршрутизации с учётом новых модулей в config/common.php:

return [
    'basePath' => dirname(__DIR__),
    'bootstrap' => ['log'],
    'aliases' => [
        '@bower' => '@vendor/bower-asset',
        '@npm'   => '@vendor/npm-asset',
    ],
    'components' => [
        'db' => [
            'class' => 'yii\db\Connection',
            'charset' => 'utf8',
        ],
        'urlManager' => [
            'class' => 'yii\web\UrlManager',
            'enablePrettyUrl' => true,
            'showScriptName' => false,
            'rules' => [
                '' => 'main/default/index',
                'contact' => 'main/contact/index',
                '<_a:(about|error)>' => 'main/default/<_a>',
                '<_a:(login|logout)>' => 'user/default/<_a>',
 
                '<_m:[\w\-]+>' => '<_m>/default/index',
                '<_m:[\w\-]+>/<id:\d+>' => '<_m>/default/view',
                '<_m:[\w\-]+>/<id:\d+>/<_a:[\w-]+>' => '<_m>/default/<_a>',
                '<_m:[\w\-]+>/<_c:[\w\-]+>' => '<_m>/<_c>/index',
                '<_m:[\w\-]+>/<_c:[\w\-]+>/<id:\d+>' => '<_m>/<_c>/view',
                '<_m:[\w\-]+>/<_c:[\w\-]+>/<id:\d+>/<_a:[\w\-]+>' => '<_m>/<_c>/<_a>',
                '<_m:[\w\-]+>/<_c:[\w\-]+>/<_a:[\w-]+>' => '<_m>/<_c>/<_a>',
            ],
        ],
        'mailer' => [
            'class' => 'yii\swiftmailer\Mailer',
        ],
        'cache' => [
            'class' => 'yii\caching\DummyCache',
        ],
        'log' => [
            'class' => 'yii\log\Dispatcher',
        ],
    ],
    'params' => $params,
];

Теперь исправим маршруты ссылок в главном меню views/layouts/main:

echo Nav::widget([
    'options' => ['class' => 'navbar-nav navbar-right'],
    'items' => [
        ['label' => 'Home', 'url' => ['/main/default/index']],
        ['label' => 'Contact', 'url' => ['/main/contact/index']],
        Yii::$app->user->isGuest ?
            ['label' => 'Login', 'url' => ['/user/default/login']] :
            ['label' => 'Logout (' . Yii::$app->user->identity->username . ')',
                'url' => ['/user/default/logout'],
                'linkOptions' => ['data-method' => 'post']],
    ],
]);

Если всё сделано верно, то снаружи у приложения ничего не изменится. Все страницы будут работать как и раньше. Но теперь и вы можете испытать то волшебное чувство, когда заказчик на вас кричит: «Жулики! Хватит меня обманывать! Видимость работы создавать! Целый день просидели, ничего не сделали! Вообще! Верните мои деньги!».

А сейчас, как всегда, задавайте вопросы, излагайте эмоции и вносите предложения в комментариях.

Часть 3: Перенос пользователей в базу данных

Комментарии

 

nlan

У Вас в коде ошибочка, судя по всему:

'<_m:[\w\-]+>' => '<_m><_c>/default/index',

Не хватает слеша между модулем и контроллером.

Спасибо за статью, жду продолжения

Ответить

 

Дмитрий Елисеев

Исправил. Спасибо!

'<_m:[\w\-]+>' => '<_m>/default/index',
Ответить

 

Alex

Дмитрий, расскажите, пожалуйста, как Вам удалось победить проблему со отображением шрифтов в PhpStorm под Ubuntu?

Ответить

 

Дмитрий Елисеев

Загружаем шрифт Tahoma из под sudo. Вроде бы так:

aptitude install msttcorefonts
cd~
wget http://www.stchman.com/tools/MS_fonts/tahoma.zip
unzip -d /usr/share/fonts/truetype/msttcorefonts ~/tahoma.zip
fc-cache -f -vrm -f ~/tahoma.zip

Потом в PhpStorm идём Settings - IDE Settings - Apperance, там ставим галочку Override default fonts by и выбираем Tahoma 11. Весь интерфейс поменяется.

Потом там же IDE Settings - Editor - Colors & Fonts - Font выбираем Primary Font в Surce Code Pro 12

Ответить

 

Alex

Спасибо!

Ответить

 

Papin

I think that we need to change

namespace app\models;

line to

namespace app\modules\contact\models;

in /app/modules/contact/models/ContactForm.php file.

Ответить

 

bobpps

Большое спасибо, ждем продолжения!

Ответить

 

minority

Спасибо,ждем продолжения!

Ответить

 

Дмитрий Ермола

Как всегда отличная статья! Спасибо.
Есть небольшое предложение: рассмотреть (сравнить) аутентификацию и использование одной части проекта несколькими (2 - 3-мя) ролями и через accessFilter и через RBAC.

Ответить

 

Сергей Доровский

А будет набор учеников для освоения Git и Composer? Хотелось бы конкретно разобраться в этих темах.
Или может цикл статей посвящённых этим темам?

Ответить

 

Дмитрий Елисеев

Да, планирую провести несколько занятий. Со сроками ещё не решил (это зависит от текущих проектов и затрат времени на подготовку). Подпишитесь пока на новости, чтобы не пропустить анонс и бонусы.

Ответить

 

Олег

Нет в yii такой директории "layiouts"!

Ответить

 

Олег

layouts есть, а "layiouts" - нет!

Ответить

 

Anton Kalny

Очень остроумно, однако.

Ответить

 

Иван

Когда же будет продолжение? Как раз самое интересное, что хотелось бы увидеть - перенос пользователей в бд.

Ответить

 

Дмитрий

Так уже давно статья висит. Ссылка в конце этой статьи.

Ответить

 

Дмитрий Елисеев

Ну так продолжение-то со ссылкой только 14-го октября появилось :)

Ответить

 

Тимур

Дмитрий, большое спасибо за ваши статьи.
Есть один вопросик с которым разобраться не могу.
При выполнении site.name/gii, вижу сообщение:

Forbidden (#403)
You are not allowed to access this page.

Я так понимаю, где-то не указан "allowedIPs" и сам разобраться не могу, может подскажете? :)

Ответить

 

Виталий

Возникла та же проблема. У меня приложение установлено не на локлахосте, а на сервере. Из нагугленного помогло дописать 'allowedIPs' в config/web.php:

 if (YII_ENV_DEV) {
    $config['bootstrap'][] = 'debug';
    $config['modules']['debug'] = 'yii\debug\Module';

    $config['bootstrap'][] = 'gii';
    $config['modules']['gii'] = [
        'class' => 'yii\gii\Module',
        'allowedIPs' => ['127.0.0.1', 'Мой IP'],
    ];
}
Ответить

 

Timur

Да, аналогичная схема.
Большое спасибо за помощь.

Ответить

 

Сергей

Здравствуйте. Начал учить yii2 и вот первая трудность) Почему-то модули из коробки, которые прописаны в конфиге не видятся приложением. Например, на стартовой странице внизу пишет Not Found
The requested URL /seo-yii2/project/web/debug/default/toolbar was not found on this server.
Пытаюсь зайти в гии и тоже страница не найдена - localhost/seo-yii2/project/gii
Как же быть?

Ответить

 

Сергей

Извиняюсь за глупый вопрос. Уже сам разобрался) Нужно было в .htaccess убрать index.php

Ответить

 

Дмитрий

Дмитрий, подскажите, плиз.
На винде в опенсервере все работало, но что-то винда последнее время стало очень сильно бесить. Установил ubuntu, yii работает, но вот при авторизации юзера стала вылетать ошибка:

PHP Notice – yii\base\ErrorException
Undefined property: app\modules\user\models\User::$authKey

public function getAuthKey()
{
    return $this->authKey;
}

обновляешь страницу или просто нажимаешь назад и юзер авторизован и скрипт продолжает работать в штатном режиме. Причем эта ошибка вылетает когда устанавливаешь чекбокс "Запомнить меня". Если галочку не ставить, то юзер авторизируется без проблем. Никак не могу побороть эту ошибку.

Ответить

 

Дмитрий Елисеев

Ну раз метод есть и на других системах работает, то это врядли в Yii дело.

Ответить

 

Дмитрий

Это понятно, может в линуксе как-то по другому работа с сессиями устроена или может еще чего не хватает? Может кто подскажет?

Ответить

 

Юрий

"Теперь дело за контроллером SiteController. Его содержимое необходимо разнести по трём контроллерам DefaultController всех модулей."
- не совсем понял как это делалось. Просто копи-паст? (так не открывается потом страница).

Ответить

 

Дмитрий Елисеев

Да, перемещение действий и представлений с изменением маршрутов в конфигурационном фале и меню шаблона. Код всех новых контроллеров приведён.

Ответить

 

Иван

Дмитрий, никак не могу понять, а зачем вообще все это делать? Я имею ввиду создавать под метод из контроллера целый модуль. Можно для тех, кто в танке популярно, что мы выиграли таким образом?

Ответить

 

Дмитрий Елисеев

С модулями ничего не выиграли. Просто удобно разложили по папкам.

Ответить

 

Иван

Вы не подумайте, что я с претензией. Очевидно, вы более опытный разработчик, чем я, просто правда не понимаю...

в моем понимании модуль - это такая здоровенная часть сайта, например Админка, Форум, Магазин, Доска объявлений или еще что-то в этом духе, а мы по разным модулям разложили три простых метода, которые у нас были в одном контроллере. Мне кажется это все равно что с танка по воробьям стрелять, не?

Ответить

 

Дмитрий Елисеев

Это просто сейчас там по одному методу. Потом в contact добавим свою модель, в user добавим просмотр и редактирование своего профиля и сделаем большой модуль панели управления. Хотя contact можно не делать. Ещё подумаю по этому поводу.

Ответить

 

Дмитрий Елисеев

Всё-таки оставил два модуля main и user. Перенёс всё из модуля contact обратно в main.

Ответить

 

CalvinKlein

По-моему раньше было лучше =|
С рассчетом на расширение функционала.

Ответить

 

Дмитрий Елисеев

Удалил действие actionAbout и его представление about.php за ненадобностью.

Ответить

 

Дмитрий Елисеев

Добавил loginUrl в конфигурацию для компонента user.

Ответить

 

Страшила – kripipasta.com

> добавлена секция modules с тремя модулями
Там только два модуля :)

Ответить

 

Дмитрий Елисеев

Забыл поменять, когда переделывал с трёх на два. Исправил.

Ответить

 

CalvinKlein

"Вспомним, что в config/web-local.php у нас находится фрагмент: ..."

Этот фрагмент в config/web.php

Ответить

 

Дмитрий Елисеев

Спасибо! Исправил.

Ответить

 

Тимофей

А можете написать, как разрабатывать модули для yii2 как composer-пакеты?

Ответить

 

Дмитрий Елисеев

Может быть.

Ответить

 

Vic

Порядок правил в urlManager

'<_m:[\w\-]+>' => '<_m>/default/index',
'<_m:[\w\-]+>/<_c:[\w\-]+>' => '<_m>/<_c>/index',

не надо поменять?

Ответить

 

Дмитрий Елисеев

Нет, иначе будет генерироваться /blog/default вместо /blog.

Ответить

 

Pavel Lenik

Подскажите пожалуйста, я создал модуль admin, но он не работает, видео с кодом модуля http://youtu.be/esDkfh1ZyFw

Ответить

 

Дмитрий Елисеев

Попробуйте адрес ?r=admin/default/index

Ответить

 

Александр – nix-tips.ru

Дмитрий, Вы купили лицензию phpstorm и пользуетесь легально?

Сотня зеленых - не так уже мало, по текущему курсу. Нелегально пользоваться принципиально не хочу. Поэтому продолжаю пользоваться netbeans:)

Ответить

 

Дмитрий Елисеев

Да, купил. Но ещё по старому курсу.

Ответить

 

Andrei K.

https://confluence.jetbrains.com/display/PhpStorm/PhpStorm+Early+Access+Program
Здесь можете делать все легально не покупая. Но придется регулярно обновлять и иногда ловить баги. Но на практике бывает очень редко.
Я тоже купил лицензию, но все равно сижу тестирую.

Еще у них часто бывают огромные скидки, следите за их новостями.
Вообще весч достойная, особенно 9 версия, я бы на месте всех ребятам отдал денег, тем более что компания российская, хоть и зарегистрирована в Европе.

Ответить

 

Павел Новиков

Добрый день. Пытаюсь открыть генератор по этому адресу localhost/gii но ничего не получается. В смысле браузер говорит что такой директории нет. Установлен Yii2+Composer на XAMPP

Ответить

 

Дмитрий Елисеев

В web/index.php поставьте окружение dev:

defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'dev');
Ответить

 

Никита

У меня тоже была подобная проблема - всё настроено так, как прописано в этой статье, но не работает. Тоже использую пакет, но MAMP Pro.

Гугл не помог. Решение нашёл только в комментах в файле:
vendor/yiisoft/yii2-gii/Module.php

* If your application enables [[\yii\web\UrlManager::enablePrettyUrl|pretty URLs]],
* you can then access Gii via URL: `localhost/path/to/index.php/gii`

= адрес до генератора Gii будет таким:

localhost/index.php/gii

Ответить

 

Никита

+ если порт в пакете MAMP или аналогичном отличается от 80, то нужно ещё добавить порт и будет что-то вроде:

localhost:8888/index.php/gii

Ответить

 

Никита

Но на самом деле проблема в какой-то моей ошибке во время выполнения инструкций из статьи: когда переделал всё заново, то всё нормально стало работать :)

Ответить

 

Александр Маковкин

Добрый день.
а как подключить свою тему в модуле?
создаю модуль для админки.
нужно прописать свой путь для layouts

спасибо

Ответить

 

Никола

Контроллер обратной связи сделаем отдельным в модуле main:

Глупый вопрос от нуба, но в какой файлик это писать?

Ответить

 

Дмитрий Елисеев

modules/main/controllers/ContactController.php

Ответить

 

Никола

Спасибо

Ответить

 

des

Можно было б не старые перекидывать, а новый модуль создать (admin тот же). ну и в него для практики вкатить модель роли, ну или там по вкусу

Ответить

 

Nick

Добрый день.

Сделал как Вы описали на базовом приложении, главная страница открывается, на остальные сервер выдает ошибку 404 (как я понимаю не находит контроллеры). Поигрался с правилами УрлМанагер, проверил все еще раз, вроде все верно. Хочется знать, в чем может быть причина?

Ответить

 

Nick

Например страница обратной связи доступна по адресу /index.php/main/contact/index
хотя 'showScriptName' => false
Так же и все остальные, без /index.php/ маршрут не работает.

Ответить

 

Дмитрий Елисеев

Вероятно, что проблема c web/.htaccess

Ответить

 

Олег

Внес изменения в проект, описанные в этой статье, при попытке открыть главную страницу получаю такой вывод:

An Error occurred while handling another error:
exception 'yii\base\InvalidRouteException' with message 'Unable to resolve the request "main/default/error".'

Никак не могу найти где накосячил. Подскажите куда посмотреть?

Ответить

 

Дмитрий Елисеев

А в DefaultController есть метод подключения действия?

public function actions()
{
    return [
        'error' => [
            'class' => 'yii\web\ErrorAction',
        ],
    ];
}
Ответить

 

Олег

Да такой метод есть.

Ответить

 

Дмитрий Елисеев

А сам модуль в конфиге подключён?

Ответить

 

Олег

да проблема была именно в конфиге, я по ошибке в params.php модули пытался подключить.
Спасибо за то, что отвечаете :)

Ответить

 

Сергей

main/default/index
This is the view content for action "index". The action belongs to the controller "app\modules\main\controllers\DefaultController" in the "main" module.

You may customize this page by editing the following file:
/var/www/html/basic/modules/main/views/default/index.php


Вроде все делал по инструкции, проверил уже на два раза. В чем мб проблема?

Ответить

 

Дмитрий Елисеев

Сюда должно быть перенесено представление из views/site/index.php.

Ответить

 

Сергей

Спасибо. Разобрался, все работает

Ответить

 

Дмитрий Елисеев

Попробовал перенёсти указание модулей из config/common.php в config/web.php.

Ответить

 

Uterm

А почему вы так делали? Вдруг из консоли надо будет что-нибудь в модуле admin сделать?

Ответить

 

Дмитрий Елисеев

Привязаны к модулю только конроллеры, которые в консольных коммандах без надобности. А модели и сервисы можно и без самих модулей напрямую использовать.

Ответить

 

Дмитрий Елисеев

Вернул модули в common.php обратно.

Ответить

 

Слава – unicweb.com.ua

У кого под убунту установлен Apache (вторая версия) - нужно отдельно активировать mod_rewrite, чтобы можно было использовать .htaccess. Делается это так:

$ sudo a2enmod rewrite && sudo service apache2 restart
Ответить

 

Uterm

Я правильно понимаю, что все модели которые будут участвовать в более чем одном модуле надо теперь сохранять в app\models ? Вы просто заменяете связь между модулями связями модулей к приложению, так? В чем плюсы?

Ответить

 

Дмитрий Елисеев

Подумал... и вернул обратно в модуль.

Ответить

 

Den

Дима, привет.
Возникла ошибка при переносе user в отдельный модуль в шаблоне advanced в common

Invalid Configuration – yii\base\InvalidConfigException
User::identityClass must be set.

Подскажешь где копать?

Указал в /common/config/main.php

'modules' => [
    'user' => [
        'class' => 'common\modules\user\Module',           
        'enableAutoLogin' => true,          
    ],
],

Пробовал /common/modules/user/Module в init() identityClass указать - та же ошибка

В конфиге может user identityClass отдельно еще указывать надо?

Ответить

 

Den

Извини, если напрягаю, нашел такой способ:
указал в main конфиге

'components' => [
    'user' => [
        'class' => 'yii\web\User',
        'enableAutoLogin' => true,
        'identityClass' => 'common\modules\user\models\User', 
        'loginUrl' => ['/user/default/login']
    ]
]

вроде все работает, такой способ не противоречит религии? :)
просто 2 раза в разных местах конфига указывать на user как-то избыточно, имхо

Ответить

 

Дмитрий Елисеев

В components нужно, а в modules нет:

'modules' => [
    'user' => [
        'class' => 'common\modules\user\Module',         
    ],
],
'components' => [
    'user' => [
        'class' => 'yii\web\User',
        'enableAutoLogin' => true,
        'identityClass' => 'common\modules\user\models\User', 
        'loginUrl' => ['/user/default/login']
    ]
],
Ответить

 

Den

понял, спасибо

Ответить

 

Максим

Спасибо за эти уроки все очень понятно расписано.

Ответить

 

Anton Kalny

Спасибо за статьи!

Ответить

 

Вадим

>>"К нему нужно перенести все его представления index.php и error.php из папки views/site в папку modules/main/default."

Тут нет ошибки? Я так думаю, подразумевалось перенести в папку "modules/main/views/default"?
Или я что-то не так просто понял?

Ответить

 

Дмитрий Елисеев

Да так. Исправил. Спасибо!

Ответить

 

Анатолий

Дмитрий, спасибо за ваш труд по созданию такого замечательного образовательного материала. Хотелось бы узнать, какие инструменты вы используете для дебага кода, используете ли вы docker?

Ответить

 

Дмитрий Елисеев

Может иногда посмотрю print_r и всё. Стараюсь код таким делать, чтобы дебажить никогда не приходилось :) А если хостер предоставляет свою виртуализацию с API, то Docker не особо нужен.

Ответить

 

Сергей

Супер!!!!

Ответить

 

Сергей Беловенцев

Доброго дня Дмитрий начал изучения Ваших уроков. Спасибо за них. Делюсь проблемой. При первом переходе на Gii к примеру по адресу site.ru/web/gii выдал ошибку.

symlink(): Cannot create symlink, error code(1314)

я поменял "linkAssets' => true," на false ошибка изчезла вернул опять на true все работает. Понимаю, что лучше не трогать но все же не подскажите что это такое ?

Ответить

 

Дмитрий Елисеев

В Windows символические ссылки не работают.

Ответить

 

Алекс

И как тогда работать с Gii на open server на Windows?
Выдает "symlink(): Cannot create symlink, error code(1314)" и все тут.
Причем такой проблемы нет у advanced версии Yii2. Там с Gii работаю без проблем.
Как побеждать?

Ответить

 

Алекс

хмм. Нашел неожиданное решение проблемы. OpenServer надо запускать от имени администратора.

Ответить

 

Сергей Беловенцев

поместил

views/site/contact.php

как

modules/main/views/contact/index.php 

выдает ошибку

Fatal error: Using $this when not in object context in E:\serv\...\modules\main\views\contact\index.php on line 11

Не подскажите в чем проблема

Ответить

 

Дмитрий Елисеев

Не открывайте представление напрямую в браузере. Открывайте адрес контроллера.

Ответить

 

Сергей Беловенцев

по адресу

http://basic.lc/modules/main/contact

или

http://basic.lc/main/contact

ошибка 404

Ответить

 

Дмитрий Елисеев
http://basic.lc/contact
Ответить

 

Сергей Беловенцев

404

Ответить

 

Дмитрий Елисеев

Поставьте в главном меню ссылку ['/main/contact/index'] и по ней перейдите.

Ответить

 

Сергей Беловенцев

Спасибо, получилось, а почему так? Не подскажите?

Ответить

 

Дмитрий Елисеев

Значит адрес другой.

Ответить

 

Егор

"Теперь дело за контроллером SiteController. Его содержимое необходимо разнести по контроллерам всех модулей."
После этого всё полетело
если ввести адрес:

http://localhost/yii2study/project/web/
Unknown Property – yii\base\UnknownPropertyException
Setting unknown property: yii\web\Application::main

http://localhost/yii2study/project/controllers/SiteController.php
Fatal error: Class 'yii\web\Controller' not found in C:\xampp\htdocs\yii2study\project\controllers          \SiteController.php on line 13

http://localhost/yii2study/project/modules/main/views/contact/
Fatal error: Using $this when not in object context in C:\xampp\htdocs\yii2study\project\modules\main\views\contact\index.php on line 12

http://localhost/yii2study/project/modules/main/models/ContactForm.php
Fatal error: Class 'yii\base\Model' not found in C:\xampp\htdocs\yii2study\project\modules\main\models\ContactForm.php on line 13

localhost/yii2study/project/modules/main/controllers/ContactController.php
Fatal error: Class 'yii\web\Controller' not found in C:\xampp\htdocs\yii2study\project\modules\main\controllers\ContactController.php on line 9

http://localhost/yii2study/project/modules/main/contact/index
Object not found!

The requested URL was not found on this server. If you entered the URL manually please check your spelling and try again.

If you think this is a server error, please contact the webmaster.
Error 404
Ответить

 

Дмитрий Елисеев
yii\web\Application::main

Модули описываем в секции 'modules' в конфиге, а не прямо наверху.

http://localhost/yii2study/project/controllers/SiteController.php

Классы так напрямую никогда не вызываются. Настройте сервер на yii2study/project/web/ и вызывайте:

http://localhost/main/contact/index

либо работайте без ЧПУ:

localhost/yii2study/project/web/index.php?r=main/contact/index
Ответить

 

Егор

в common.php про модули есть

'modules' => [
    'main' => [
        'class' => 'app\modules\main\Module',
    ],
    'user' => [
        'class' => 'app\modules\users\Module',
    ],
],
Ответить

 

Дмитрий Елисеев

Поищите, где ещё в конфигах есть main.

Ответить

 

Егор

добавил тоже самое в web.php, больше нигде нет в config

Ответить

 

Егор

вообщем откатил всё чуток назад, застрял вот на этом моменте "Теперь файл views/site/contact.php поместим как modules/main/views/contact/index.php"
После этого переход по ссылке : localhost/yii2study/project/web/index.php?r=main/contact/index выводит просто главную страницу

Ответить

 

Егор

а если щёлкунуть на contact выводит: Invalid Parameter – yii\base\InvalidParamException
The view file does not exist: C:\xampp\htdocs\yii2study\project\views\site\contact.php (оно и понятно), я видимо что-то где-то не дописал?

Ответить

 

Егор

Вот что я сделал:

в modules/main/views/contact/index.php добавил

namepace app\main\contact;
use Yii;

в views/layouts/main.php изменил

['label' => 'Contact', 'url' => ['/site/contact']],

на

['label' => 'Contact', 'url' => ['/main/contact']],

сделал новые ссылки на капчу

Теперь при переходе на localhost/yii2study/project/web/main/contact всё работает. Так ли я сделал?

Ответить

 

Дмитрий Елисеев
'url' => ['/main/contact/index']
Ответить

 

Егор

проделал тоже самое с остальными страницами, не сработало на login,
в modules/user/views/default/login.php нэймспэйса нет, а ссылка из main.php работет только если адрес /user/default/login, почему так?

Ответить

 

Егор

всё понял, просто не дочитал дальше)

Ответить

 

Сергей

Всем привет! Пошагово копирую всё со статьи (только конфиг не стал разделять). Всё работает, только капча не отображаеся (картинка отсутствует, хотя в исходном коде ссылка имеется "/main/contact/captcha?v=56ef9f3830f0f"). Всё перепроверил, в phpinfo(): gd enabled, кодировки везде utf-8. Какие еще могут быть варианты?
Автору отдельное спасибо за его труд!

Ответить

 

Дмитрий Елисеев

А если по этому адресу перейти, то что выводится?

Ответить

 

Дмитрий Елисеев

Что по адресу /main/contact/captcha?v=56ef9f3830f0f ?

Ответить

 

Дмитрий Елисеев

А на свежеустановленном yii2-app-basic в разделе Contact она выводится?

Ответить

 

Сергей

Да, выводится

Ответить

 

Сергей

Перенес в только созданное приложение yii2-app-basic конфигурацию, папку modules и шаблон main.php - капча снова не выводится.

Ответить

 

Дмитрий Елисеев

Либо в контроллере 'captcha' в actions() не перенесли, либо ещё что-то.

Ответить

 

Kumidzeke .

Добрый день.
Столкнулся с той же проблемой, не выводится картинка капчи.

На свежеустановленном выводится отлично.

В контроллере 'captcha' в actions() перенесена.

Ответить

 

Kumidzeke .

Решение моей проблемы.
Я в config/common.php забыл удалить 'params' => $params,

Удалил и все ок, captcha отображается.

Ответить

 

Сергей

Здравствуйте Дмитрий. Я начал изучать Yii по разным видео, статьям и в частности по вашим урокам. У меня возник небольшой вопрос, по поводу структуры проекта на Yii, не важно блог это или магазин. Допустим у нас есть, какая-то определенная тема (them). Что должно входить в структуру данного каталога? Ну например там будет папка с представлениями (header, footer), а что на счет представлений отдельных модулей, таких как модуль обратной связи или модуль регистрации и прочее? Их файлы должны быть отдельно в каждом модуле? Или вот content-часть, например - $content, эта часть может генерироваться разными модулями, соответственно представления этой части не имеют отношения к теме? То есть, допустим, модуль вывода статей будет иметь свое представление modules/news/views/default/index.php ? и просто при вызове экшена подключаться к лэйауту? А сам лэйаут должен лежать в папке темы? Просто я структуру общую не могу уловить. В том же Wordpress, тема полностью отделена от остального кода, то есть есть файлы движка и есть тема, тема содержит в себе все модули, скрипты, и прочее. А тут получается, что идет приложение внутри которого идут отдельно модули, потом идут какие-то отдельные контролеры, модели и представления и еще во всем этом лежит папка thems, в которую на сколько я понимаю, кроме представлений футера и хедера и положить то нечего. Или я что-то не так понимаю? Просто даже из того, что у вас представлено в этой статье, получается, что мы структуру приложения делим на модули, то есть на каждое действие, есть свой модуль и у каждого модуля есть свое представление, а что же все таки остается под файлы темы? Что из себя представляет тема в yii2 ? Извините, что вопрос не по данной теме. Буду благодарен за объяснение.

Ответить

 

Сергей

При этом я понимаю прелесть использования модулей и следование принципу единственной обязанности. То есть все в принципе удобно, делим на модули, каждый модуль отвечает за что-то свое,но я честно говоря не понимаю, что тогда будет храниться в папке темы (them), и допустим в общих папках контроллеров, моделей и представлений - frontend/controllers, frontend/models, frontend/views? Если все раскидано по модулям, то получается эти папки не нужны?

Ответить

 

Дмитрий Елисеев

Да, в отличие от WordPress темы в Yii2 применимы только к представлениям. То есть нужны практически только для хранения самого шаблона (layout) со своими стилями и изображениями. При желании можно переопределить некоторые представления из views модулей, но это мало когда пригождается.

Чаще всего темизация используется в случае, когда используется чужой модуль, установленный в vendor, и хочется немного изменить его интерфейс. В этом случае настраиваем считывание наших представлений вместо оригинальных.

А если раскидаете модели, контроллеры и представления по модулям, а layout перенесёте в тему, то пустые папки можно удалить (как в этой статье и сделано).

Ответить

 

Сергей

Спасибо большое за ответ! А то я уже начал было думать, что я неправильно понимаю суть всего этого)

Ответить

 

Игорь

Пожалуйста, объясните почему появился модуль Main? Почему не остался просто siteController, в котором можно было бы также оставить методы login, logout и так далее.

Ответить

 

Дмитрий Елисеев

Да, можно оставить SiteController с действиями index и error.

Ответить

 

Игорь

Но! Как это отразится при расширении проекта.

Ответить

 

Дмитрий Елисеев

Смотря как будете расширять.

Ответить

 

Игорь

Трудно не согласиться)

Ответить

 

Александр

Дмитрий, скажите пожалуйста, вы после того как используете gii генератор на удалённом серваке, всегда синхронизируете в PhpStorm нужные дириктории с локальным проектом? как можно упростить задачу чтобы автоматически всё обновлялось? или лучше если пользоваться генератором, то подрубить еще и openserver на локалке и не лить туда-сюда?

Ответить

 

Дмитрий Елисеев

Не использую ничего на удалённом, так как всегда разрабатываю на локальном.

Ответить

 

Igor

OpenServer + Win10 даю лінк в хроме: drive/gii (или drive/index.php/gii)
Не запускаицца gii:

PHP Warning – yii\base\ErrorException

symlink(): Cannot create symlink, error code(1314)
1. in E:\OpenServer\domains\Drive\vendor\yiisoft\yii2\web\AssetManager.php at line 520
Ответить

 

Igor

помог запуск опенсервера под админскими правами

Ответить

 

Алексей – zlaksudbi.ru

Огромная Вам благодарность, Дмитрий, за ваши статьи и уроки. Очень ценный материал.
Но возникла небольшая сложность. Всё делаю как изложено тут, за исключением того, что я на винде+апач и пользуюсь нагугленными .htaccess (полагаю, в них и кроется моя неприятность).
Всё работает, но в адресной строке на всех страницах после имени сайта приписывается "web". Например, site.ru/web/login. Как можно от него избавиться?

Вот содержимое htaccess в корне:

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 (.*) /web/$1
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php

А это в директории /web/

RewriteEngine On RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . index.php
Ответить

 

Дмитрий Елисеев

Настройте DocumentRoot на папку web, а не на корневую, и всё заработает.

Ответить

 

Алексей – zlaksudbi.ru

Благодарю, заработало )

Ответить

 

Ратибор Харьков

Большое спасибо, лучше начал понимать, что к чему в Yii )

Ответить

 

Ержан Шаймерденов

Спасибо большое.

Ответить

 

Александр Хомяк

Здравствуйте. Заинтересовали ваши статьи, как раз практически то что мне нужно)
Делаю более менее как в уроках, кроме того что исключаю создание модуля main.
Но на данный момент возникла проблема:
Вот правило urlManager

'<_a:(login|logout|signup|email-confirm|password-request-reset|password-reset)>' => 'user/default/<_a>',


но когда захожу на данные страницы site.ru/login выдает 404, а захожу на site.ru/user/default/login/ работает.
Вопрос такой: как мне избавиться от user/default/ ?

Ответить

 

Sans

Чет перестало все работать, после переделки на модули.
В конфиге:

'defaultRoute' => 'main/default/index',
...
 'modules' => [
        'main' => [
            'class' => 'app\modules\main\Module',
        ],
        'user' => [
            'class' => 'app\modules\user\Module',
        ],
 ],
'components' => [
'urlManager' => [
            'enablePrettyUrl' => true,
            'showScriptName' => false,
            'rules' => [
                '' => 'main/default/index',
                'contact' => 'main/contact/index',
                '<_a:error>' => 'main/default/<_a>',
                '<_a:(login|logout)>' => 'user/default/<_a>',

                '<_m:[\w\-]+>' => '<_m>/default/index',
                '<_m:[\w\-]+>/<_c:[\w\-]+>' => '<_m>/<_c>/index',
                '<_m:[\w\-]+>/<_c:[\w\-]+>/<id:\d+>' => '<_m>/<_c>/view',
                '<_m:[\w\-]+>/<_c:[\w\-]+>/<_a:[\w\-]+>/<id:\d+>' => '<_m>/<_c>/<_a>',
            ],
        ],


но при заходе localhost/main/default/index и localhost/ выдает 404 Page not found.
При этом маршрут пустой - yii\base\InvalidRouteException: Unable to resolve the request "". in C:\OSPanel\domains\im.ua\vendor\yiisoft\yii2\base\Module.php:537
Куда копать? В чем проблема то?

Ответить

 

Sans

а если отключить urlManager 'enablePrettyUrl' => false и ввести полный маршрут http://localhost/index.php?r=main/default/index (модуль/контролер/действие), то выпадает
тоже самое исключение, только маршрут указывает "main/default/index".

Ответить

 

петя петрович

http://prntscr.com/j9zt0x

c этим сайтом все ясно... Ты прежде чем такие статьи создавать, научился бы сам хоть чему нибудь! Клоун! Все сделано так, как в статье, все перенесено так, как в статье... Все конечно "работает"! И естественно я виноват а не автор.....

Ответить

 

Дмитрий Елисеев

Маршрутизацию настроили?

Ответить

 

Васька

config/common.php

А откуда взялось это в basic? Не перепутали с advansed версией?

Ответить

 

Дмитрий Елисеев

Из прошлой статьи. Не перепутали.

Ответить

 

Alexander

>>Нам нужно вернуть её, но при этом не добавлять как папку проекта, а подключить как одну из системных библиотек. Переходим ниже в раздел PHP и добавляем путь в список Include path:

При этом PhpStorm индексирует css классы из bower-asset
Подскажите, как это предотвратить?

Ответить

 

Дмитрий Елисеев

В новом PhpStorm можно просто добавить vendor в список Excluded, а PHP Libraries он спарсит из composer.json и подключит сам.

Ответить

 

Alexander

Точно! Спасибо! Обожаю PhpStorm :)

Ответить

 

Васька

начерта было менять статью, не понятно... все было нормально объяснено в самом начале, а сейчас какой то бред, не описывающийся нифига...

Ответить

 

Васька

Мы исключили и папку vendor из проекта, поэтому все классы фреймворка потеряются. Нам нужно вернуть её, но при этом не добавлять как папку проекта, а подключить как одну из системных библиотек.

вот где тут скрин который был? Да и компосера не было. Как этот вендер подключить как библиотеку?????

Ответить

 

Дмитрий Елисеев

Добавил скрин от нового PhpStorm.

Ответить

 

Васька

Спасибо!

Ответить

 

Васька

Ты реально столько проблем создаешь... Я из за тебя уже 10 проектов удалил и заново начинал... Ну не можешь нормально объяснять, займись другим чем нибудь, ну не твое это программирование и учение, не твое

Ответить

 

Дмитрий Елисеев

Ну так не читайте меня. И проблем не будет.

Ответить

 

Stas Ustimenko

Из IDE мне больше всего нравится Codelobster.
В ней есть специальный плагин для разработки на Yii.

Ответить

 

Рипа Юрий

А как настроить IDE для modules, чтобы были переходы на соответствующие view модуля в вызовах $this->render ?

Ответить

 

Дмитрий Елисеев

Переходы делает не сама IDE, а плагин к ней. Так что надо поинтересоваться у автора плагина.

Ответить

 

Михаил

Добрый день, Дмитрий. Изучаю YII2 по вашим рецептам.
Столкнулся с тем, что под windows PHPStorm не хочет делать refaсtor move т.к. пути не соответствуют стандарту PSR-0. Есть ли способ решения?
Или я что-то не так делаю.
Refator rename прекрасно работает.

Ответить

 

Михаил

Решено.

Ответить

Оставить комментарий

Войти | Завести аккаунт | Войти через


(никто не увидит)





Можно использовать теги <p> <ul> <li> <b> <i> <a> <pre>