Skip to content

Commit e277b50

Browse files
committed
update step
1 parent 4d77d45 commit e277b50

File tree

5 files changed

+309
-1
lines changed

5 files changed

+309
-1
lines changed

scripts/assets/screen1.2-3.jpg

13.9 KB
Loading

scripts/assets/screen1.2-4.jpg

21.3 KB
Loading

scripts/assets/screen1.2-5.jpg

25.3 KB
Loading

scripts/assets/screen1.2-6.jpg

64.6 KB
Loading

scripts/steps/step-001.1.md

Lines changed: 309 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,314 @@ public function search($params)
548548
Для закрепления знаний, настройте фильтрацию и сортировку для дополнительного свойства `countPlanets` в модели
549549
`StarSearch` на странице <a href="/yii2-app-advanced/backend/web/index.php?r=star" target="_blank">управления моделями Star</a>.
550550

551+
#### Формы для сохранения данных
552+
553+
Вы наверное уже обратили внимание на формы для сохранения:
554+
555+
- <a href="/yii2-app-advanced/backend/web/index.php?r=star/create" target="_blank">звёзд</a>
556+
- <a href="/yii2-app-advanced/backend/web/index.php?r=planet/create" target="_blank">планет</a>
557+
- <a href="/yii2-app-advanced/backend/web/index.php?r=satellite/create" target="_blank">спутников</a>
558+
559+
Сейчас они выглядит, мягко говоря, не удобно для того, чтобы ими пользоваться.
560+
561+
Давайте начнём с первой формы - для сохранения звёзд. Смотрим на url - `star/create`, далее открываем контроллер `StarController`
562+
ищем в нём метод `actionCreate()`, видем что вызывается вид `create` - следовательно открываем `yii2-app-advanced/backend/views/star/create.php`
563+
И обнаруживаем, что в этом файле нет нам уже знакомого класса `ActiveForm` для работы с формами. А есть:
564+
565+
```php
566+
$this->render('_form', ['model' => $model,])
567+
```
568+
569+
Как уже известно в видах $this - это <a href="http://www.yiiframework.com/doc-2.0/yii-web-view.html" target="_blank">yii\web\View</a>.
570+
Метод `render`, вам встречался в контроллере, но там его реализация отличается тем, что до вывода вида, вызывается компонент
571+
для работы с видами, т.е. вызывается `yii\web\View` и только затем его метод для получения вида из файла. Тут же `$this`
572+
это уже компонент для работы с видами и его `render()` получает вид `_form`. Получается, что один вид `_form` находится внутри
573+
другого вида `create`. Почему это так? Это удобно, так как вид `_form` содержит форму, которая может быть использована не
574+
только для создания модели, но и также для изменения уже существующей модели. Если открыть вид `update.php` в этой же директории
575+
то, можно обнаружить тот же код, что в и `create.php`:
576+
577+
```php
578+
$this->render('_form', ['model' => $model,])
579+
```
580+
581+
Т.е. одна форма используется для видов `create.php` и `update.php`.
582+
583+
Открыв `yii2-app-advanced/backend/views/star/_form.php` вы не обнаружите ничего нового. Обычная форма - одно поле и кнопка.
584+
В браузере эта форма занимает почти всю ширину экрана. Не совсем красиво. У
585+
<a href="http://www.yiiframework.com/doc-2.0/yii-bootstrap-activeform.html" target="_blank">ActiveForm</a>, что из пространства имён
586+
`yii\bootstrap\` есть свойство `$layout`, которое может иметь значение `['default', 'horizontal', 'inline']`. Попробуйте
587+
установить inline. В `views/star/_form` :
588+
589+
```php
590+
<?php $form = ActiveForm::begin(['layout'=>'inline']); ?>
591+
```
592+
593+
Не забудьте изменить `yii\widgets\ActiveForm;` на корректное пространство. Обновите страницу с формой и посмотрите
594+
на результат. Красивее, чем было, хотя о вкусах не спорят. При `inline` можно заметить, что метки (label) не используются.
595+
Но есть <a href="http://htmlbook.ru/html/input/placeholder" target="_blank">placeholder</a>. Добавим его к `textInput`:
596+
597+
```php
598+
<?= $form->field($model, 'name')->textInput(['maxlength' => 255, 'placeholder'=>'Введите название звезды']) ?>
599+
```
600+
601+
С Yii версии 2.0.3 `maxlength` - максимальная длина введённого текста, может быть автоматически высчитана из правила валидации:
602+
603+
```php
604+
public function rules()
605+
{
606+
return [
607+
[['name'], 'required'],
608+
[['name'], 'string', 'max' => 255]
609+
];
610+
}
611+
```
612+
613+
Для этого используйте:
614+
615+
```php
616+
<?= $form->field($model, 'name')->textInput(['maxlength' => true, 'placeholder'=>'Введите название звезды']) ?>
617+
```
618+
619+
Теперь наша форма готова. Введите название для звезды и нажав кнопку создать, почувствовать себя властелином Вселенной.
620+
<img src="/scripts/assets/screen1.2-3.jpg" class="img-responsive">
621+
622+
Открыв `\backend\controllers\StarController::actionCreate` вы увидите уже знакомый принцип сохранения данных - проверка
623+
и дальнейшая их сохранение. Со звездой всё просто. Перейдём к <a href="/yii2-app-advanced/backend/web/index.php?r=planet/create" target="_blank">планетам</a>.
624+
625+
На форме с планетами появляется новое поле - `star_id`. Тот, кто будет пользоваться этой формой, будет вспоминать программиста
626+
не добрым словом. Всех id не упомнишь, да и ошибиться всегда можно. Давайте сделаем выпадающий список с названиями звёзд.
627+
628+
Как мы делали когда-то для планет:
629+
630+
```php
631+
<?= $form->field($model, 'planet')->dropDownList(
632+
['Меркурий', 'Венера', 'Земля', 'Марс', 'Юпитер', 'Сатурн', 'Уран', 'Нептун']
633+
) ?>
634+
```
635+
636+
Только вместо массива будем ипользовать запрос `Star::find()->all()`, который вернёт массив моделей.
637+
638+
```php
639+
$stars = [];
640+
641+
foreach (Star::find()->all() as $star){
642+
$stars[$star->id] = $star->name;
643+
}
644+
645+
echo $form->field($model, 'planet')->dropDownList($stars);
646+
```
647+
648+
Но можно переписать этот код с использованием класса помощника <a href="http://www.yiiframework.com/doc-2.0/yii-helpers-arrayhelper.html" target="_blank">
649+
yii\helpers\ArrayHelper</a>, который позволяет обращаться с массивами более эффективно:
650+
651+
```php
652+
$stars = ArrayHelper::map(Star::find()->all(), 'id', 'name');
653+
echo $form->field($model, 'planet')->dropDownList($stars);
654+
```
655+
656+
Конечно, вы можете обойтись без переменной `$stars`, записав этот код одну строку. Ну и после всего, для этой формы
657+
попробуйте использовать `horizontal`:
658+
659+
```php
660+
<?php $form = ActiveForm::begin(['layout' => 'horizontal',]); ?>
661+
```
662+
663+
<img src="/scripts/assets/screen1.2-4.jpg" class="img-responsive">
664+
665+
Осталось <a href="/yii2-app-advanced/backend/web/index.php?r=satellite/create" target="_blank">форма создания спутников</a>.
666+
Вы уже всё умеете, чтобы внести изменения самостоятельно.
667+
668+
<img src="/scripts/assets/screen1.2-5.jpg" class="img-responsive">
669+
670+
Так как формы, для редактирования существующих моделей используются одни и те же, что и для сохранения. То, что-либо новое
671+
создавать не нужно.
672+
673+
#### Сохранение реляционных данных.
674+
675+
У нас есть три формы для трёх разных моделей. Представьте себе ситуацию: нужно ввести информацию по новой планете, но звезды
676+
у неё ещё нету. Не совсем удобно переключаться с формы на форму, сохраняя новые данные. Давайте объединим работу с тремя
677+
формами в одной. Для этого нам понадобится новая модель формы, которая будет объединять работу с тремя моделями
678+
относительно модели Планет.
679+
680+
Давайте начнём с теста. Это упростит отладку и разработку. Будем двигаться небольшими шагами, чтобы было понятнее и легче.
681+
682+
Создадим функциональный тест `PlanetFormCept`:
683+
684+
```
685+
cd yii2-app-advanced\tests\codeception\backend\
686+
codecept build
687+
```
688+
689+
```
690+
codecept generate:cept functional PlanetFormCept
691+
Test was created in PlanetFormCept.php
692+
```
693+
694+
Откройте файл теста `PlanetFormCept.php` и измените его содержимое на:
695+
696+
```php
697+
<?php use tests\codeception\backend\FunctionalTester;
698+
/* @var $scenario Codeception\Scenario */
699+
$I = new FunctionalTester($scenario);
700+
$I->wantTo('ensure than create form works');
701+
```
702+
703+
Можно запустить этот тест:
704+
705+
```
706+
codecept run functional functional/PlanetFormCept.php
707+
708+
Time: 1.51 seconds, Memory: 13.25Mb
709+
OK (1 test, 0 assertions)
710+
```
711+
712+
Теперь к тесту добавим команду на открытие страницы с формой. Для этого нужно создать объект этой страницы.
713+
В директории `yii2-app-advanced/tests/codeception/backend/_pages/` создайте `PlanetFormPage.php`:
714+
715+
```php
716+
<?php
717+
namespace tests\codeception\backend\_pages;
718+
719+
use yii\codeception\BasePage;
720+
721+
class PlanetFormPage extends BasePage
722+
{
723+
public $route = 'planet/create';
724+
}
725+
```
726+
727+
Теперь в нашем тесте можно воспользоваться этим объектом, для того, чтобы имитировать открытие страницы с формой:
728+
729+
```
730+
//...
731+
$I->wantTo('ensure than create form works');
732+
$formPage = \tests\codeception\backend\_pages\PlanetFormPage::openBy($I);
733+
```
734+
735+
Запускаем тест для того, чтобы убедиться, что всё выполнили правильно:
736+
737+
```
738+
codecept run functional functional/PlanetFormCept.php
739+
740+
Time: 518 ms, Memory: 18.00Mb
741+
OK (1 test, 0 assertions)
742+
```
743+
744+
На данный момент форма содержит два поля: тестовое поле "Название планеты" и выпадающий список "Название звезды".
745+
Проверим их через тест. Для этого нам понадобятся методы `fillField` и `click` из класса
746+
`yii2-app-advanced/tests/codeception/backend/functional/FunctionalTester.php`, который создался с помощью ранее выполненной
747+
команды `codecept build`.
748+
749+
<p class="alert alert-info">
750+
Ознакомьтесь с информацией <a href="http://codeception.com/docs/modules/Yii2" target="_blank">по доступным методам</a>
751+
модуля Yii2 для Codeception.
752+
</p>
753+
754+
```php
755+
<?php use tests\codeception\backend\FunctionalTester;
756+
/* @var $scenario Codeception\Scenario */
757+
$I = new FunctionalTester($scenario);
758+
$I->wantTo('ensure than create form works');
759+
$formPage = \tests\codeception\backend\PlanetFormPage::openBy($I);
760+
761+
$I->fillField('//*[@id="planet-name"]','Земля');
762+
$I->selectOption('//*[@id="planet-star_id"]', 'Солнце');
763+
$I->click('//*[@id="w0"]/div[3]/button');
764+
$I->dontSeeInTitle('Новая планета');
765+
```
766+
767+
- fillField - заполняем текстовое поле "Название планеты"
768+
- click - нажимаем на кнопку "Создать"
769+
- dontSeeInTitle - проверяем, чтобы в заголовке странице не было текста "Новая планета"
770+
771+
`//*[@id="planet-name"]` и `'//*[@id="w0"]/div[3]/button'` - это <a href="https://ru.wikipedia.org/wiki/XPath" target="_blank">XPath</a>.
772+
Например, в браузере Chrome, нажав на странице с формой F12, можно получить XPath через контекстное меню к html коду элемента:
773+
774+
<img src="/scripts/assets/screen1.2-6.jpg" class="img-responsive">
775+
776+
Запустив тест, увидим ошибку:
777+
778+
```
779+
codecept run functional functional/PlanetFormCept.php
780+
781+
InvalidArgumentException: Input "Planet[star_id]" cannot take "Солнце" as a value (possible values: ).
782+
3. I select option "//*[@id="planet-star_id"]","Солнце"
783+
//...
784+
```
785+
786+
Всё потому, что используется тестовая база данных, которая никакой информации по звёздам не содержит. Данные есть только
787+
в главной базе данных, но её использовать не будем, во избежание порчи данных. На помощь приходят фикстуры. Это состояние
788+
базы данных, до которого она будет доведена при запуске теста. Для работы с фикстурами исполнитель функциональных тестов
789+
`FunctionalTester.php` использует класс помощник `FixtureHelper.php` (в файле `tests/codeception/backend/functional.suite.yml`):
790+
791+
```
792+
class_name: FunctionalTester
793+
modules:
794+
enabled:
795+
- Filesystem
796+
- Yii2
797+
- tests\codeception\common\_support\FixtureHelper
798+
config:
799+
Yii2:
800+
configFile: '../config/backend/functional.php'
801+
```
802+
803+
откройте этот файл и найдите его метод:
804+
805+
```php
806+
public function fixtures()
807+
{
808+
return [
809+
'user' => [
810+
'class' => UserFixture::className(),
811+
'dataFile' => '@tests/codeception/common/fixtures/data/init_login.php',
812+
],
813+
];
814+
}
815+
```
816+
817+
Запуская каждый раз любой функциональный тест из backend, стартует фикстура UserFixture:
818+
819+
```php
820+
class UserFixture extends ActiveFixture
821+
{
822+
public $modelClass = 'common\models\User';
823+
}
824+
```
825+
826+
, которая очищает таблицу для модели `common\models\User`. А затем заполняет её данными из `dataFile`.
827+
828+
Для кого-то, на первый раз это всё может показаться сложным и избыточным. Ведь мы ушли в сторону, а могли бы за это время
829+
уже создать необходимую форму и двигаться дальше. Но нет, топчемся на месте. Тяжело в учении - легко в бою.
830+
Сделайте паузу, выпейте кофе, расслабитесь. Когда отдохнули - продолжим.
831+
832+
Создадим новые фикстуры, которые будут сбрасывать состояние таблицы для звёзд, планет и их спутников. Создайте в
833+
`yii2-app-advanced/tests/codeception/common/fixtures` файлы:
834+
835+
- PlanetFixture.php
836+
- SatelliteFixture.php
837+
- StarFixture.php
838+
839+
Вот пример одной из фикстуры `SatelliteFixture.php`:
840+
841+
```php
842+
<?php
843+
namespace tests\codeception\common\fixtures;
844+
845+
use yii\test\ActiveFixture;
846+
847+
class SatelliteFixture extends ActiveFixture
848+
{
849+
public $modelClass = 'common\models\Satellite';
850+
}
851+
852+
```
853+
854+
Остальные две: `PlanetFixture` и `StarFixture` создайте самостоятельно.
855+
856+
857+
551858
#### Дополнительная информация для самостоятельного ознакомления:
552859

553-
- <a href="#" target="_blank">Официальное руководство по работе с связями в AR</a>.
860+
- <a href="#" target="_blank">Официальное руководство по работе с связями в AR</a>.
861+
- <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-ru/helper-array.md" target="_blank">Руководство по ArrayHelper</a>.

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy