Магазин на Yii2, часть 21. Оформление заказа, часть вторая
07.08.2019
Теги: ActiveForm • Web-разработка • Yii2 • БазаДанных • Заказ • ИнтернетМагазин • КаталогТоваров • Корзина • Практика • Форма • Фреймворк
Хорошо, форма для оформления заказа готова, правила для валидации полей заказа заданы. Осталось только сохранить в таблицу БД order
введенные пользователем данные. Поскольку у нас поля created
и updated
должны сохранять текущую дату и время, добавим метод behaviors()
для класса Order
.
<?php namespace app\models; use Yii; use yii\db\ActiveRecord; use yii\behaviors\TimestampBehavior; use yii\db\Expression; class Order extends ActiveRecord { /*...*/ /** * Метод расширяет возможности класса Order, внедряя дополительные * свойства и методы. Кроме того, позволяет реагировать на события, * создаваемые классом Order или его родителями */ public function behaviors() { return [ [ 'class' => TimestampBehavior::class, 'attributes' => [ // при вставке новой записи присвоить атрибутам created // и updated значение метки времени UNIX ActiveRecord::EVENT_BEFORE_INSERT => ['created', 'updated'], // при обновлении существующей записи присвоить атрибуту // updated значение метки времени UNIX ActiveRecord::EVENT_BEFORE_UPDATE => ['updated'], ], // если вместо метки времени UNIX используется DATETIME 'value' => new Expression('NOW()'), ], ]; } /*...*/ }
behaviors()
нет необходимости — при создании таблицы БД order
мы указали, какие значения устанавливать для полей created
и updated
при вставке новой записи и при обновлении существующей. Но хотелось продемонстрировать такую возможность фреймворка.
Теперь займемся контроллером. При отправке данных формы оформления заказа, мы должны загрузить их в модель и проверить с помощью заданных ранее правил валидации. Если данные прошли валидацию — сохраняем их в базу данных. Если при валидации были обнаружены ошибки — сохраняем в сессию введенные пользователем данные и массив сообщений об ошибках.
<?php namespace app\controllers; use Yii; use app\models\Basket; use app\models\Order; class OrderController extends AppController { public $defaultAction = 'checkout'; public function actionCheckout() { $this->setMetaTags('Оформление заказа'); $order = new Order(); /* * Если пришли post-данные, загружаем их в модель... */ if ($order->load(Yii::$app->request->post())) { // ...и проверяем эти данные if ( ! $order->validate()) { // данные не прошли валидацию, отмечаем этот факт Yii::$app->session->setFlash( 'checkout-success', false ); // сохраняем в сессии введенные пользователем данные Yii::$app->session->setFlash( 'checkout-data', [ 'name' => $order->name, 'email' => $order->email, 'phone' => $order->phone, 'address' => $order->address, 'comment' => $order->comment ] ); /* * Сохраняем в сессии массив сообщений об ошибках. Массив имеет вид * [ * 'name' => [ * 'Поле «Ваше имя» обязательно для заполнения', * ], * 'email' => [ * 'Поле «Ваш email» обязательно для заполнения', * 'Поле «Ваш email» должно быть адресом почты' * ] * ] */ Yii::$app->session->setFlash( 'checkout-errors', $order->getErrors() ); } else { /* * Заполняем остальные поля модели — те которые приходят * не из формы, а которые надо получить из корзины. Кроме * того, поля created и updated будут заполнены с помощью * метода Order::behaviors(). */ $basket = new Basket(); $content = $basket->getBasket(); $order->amount = $content['amount']; // сохраняем заказ в базу данных $order->insert(); $order->addItems($content); // очищаем содержимое корзины $basket->clearBasket(); // данные прошли валидацию, заказ успешно сохранен Yii::$app->session->setFlash( 'checkout-success', true ); } // выполняем редирект, чтобы избежать повторной отправки формы return $this->refresh(); } return $this->render('checkout', ['order' => $order]); } }
В view-шаблоне формы оформления заказа мы проверяем — были отправлены данные формы? Если да — проверяем, прошли ли данные валидацию? Если данные прошли валидацию — показываем сообщение об успешном оформлении заказа. Если нет — выводим сообщения об ошибках, опять показываем форму и заполняем ее введенными ранее данными.
<?php /* * Страница оформления заказа, файл views/order/checkout.php */ use app\components\TreeWidget; use app\components\BrandsWidget; use yii\widgets\ActiveForm; use yii\helpers\Html; use yii\helpers\Url; /* * Если данные формы не прошли валидацию, получаем из сессии сохраненные * данные, чтобы заполнить ими поля формы, не заставляя пользователя * заполнять форму повторно */ $name = ''; $email = ''; $phone = ''; $address = ''; $comment = ''; if (Yii::$app->session->hasFlash('checkout-data')) { $data = Yii::$app->session->getFlash('checkout-data'); $name = Html::encode($data['name']); $email = Html::encode($data['email']); $phone = Html::encode($data['phone']); $address = Html::encode($data['address']); $comment = Html::encode($data['comment']); } ?> <section> <div class="container"> <div class="row"> <div class="col-sm-3"> <div class="left-sidebar"> <h2>Каталог</h2> <div class="category-products"> <?= TreeWidget::widget(); ?> </div> <h2>Бренды</h2> <div class="brand-products"> <?= BrandsWidget::widget(); ?> </div> </div> </div> <div class="col-sm-9"> <h1>Оформление заказа</h1> <div id="checkout"> <?php $success = false; if (Yii::$app->session->hasFlash('checkout-success')) { $success = Yii::$app->session->getFlash('checkout-success'); } ?> <?php if (!$success): ?> <?php if (Yii::$app->session->hasFlash('checkout-errors')): ?> <div class="alert alert-warning alert-dismissible" role="alert"> <button type="button" class="close" data-dismiss="alert" aria-label="Закрыть"> <span aria-hidden="true">×</span> </button> <p>При заполнении формы допущены ошибки</p> <?php $allErrors = Yii::$app->session->getFlash('checkout-errors'); ?> <ul> <?php foreach ($allErrors as $errors): ?> <?php foreach ($errors as $error): ?> <li><?= $error; ?></li> <?php endforeach; ?> <?php endforeach; ?> </ul> </div> <?php endif; ?> <?php $form = ActiveForm::begin( ['id' => 'checkout-form', 'class' => 'form-horizontal'] ); echo $form->field($order, 'name') ->textInput(['value' => $name]); echo $form->field($order, 'email') ->input('email', ['value' => $email]); echo $form->field($order, 'phone') ->textInput(['value' => $phone]); echo $form->field($order, 'address') ->textarea(['rows' => 2, 'value' => $address]); echo $form->field($order, 'comment') ->textarea(['rows' => 2, 'value' => $comment]); echo Html::submitButton( 'Оформить заказ', ['class' => 'btn btn-primary'] ); ActiveForm::end(); ?> <?php else: ?> <p>Ваш заказ успешно оформлен, спасибо за покупку.</p> <?php endif; ?> </div> </div> </div> </div> </section>
В классе модели Order
нам еще потребуется метод addItems()
, который мы вызываем из контроллера.
<?php namespace app\models; use Yii; use yii\db\ActiveRecord; use yii\behaviors\TimestampBehavior; use yii\db\Expression; class Order extends ActiveRecord { /*...*/ /** * Добавляет записи в таблицу БД `order_item` */ public function addItems($basket) { // получаем товары в корзине $products = $basket['products']; // добавляем товары по одному foreach ($products as $product_id => $product) { $item = new OrderItem(); $item->order_id = $this->id; $item->product_id = $product_id; $item->name = $product['name']; $item->price = $product['price']; $item->quantity = $product['count']; $item->cost = $product['price'] * $product['count']; $item->insert(); } } }
- Магазин на Yii2, часть 20. Оформление заказа, часть первая
- Магазин на Laravel 7, часть 10. Форма оформления, сохранение заказа в базу данных
- Магазин на Yii2, часть 22. Оформление заказа, часть третья
- Магазин на Yii2, часть 17. Корзина покупателя, часть первая
- Магазин на Laravel 7, часть 24. Фильтр товаров категории по цене, новинкам и лидерам продаж
- Магазин на Laravel 7, часть 17. Панель управления, работа с заказами, изменение статуса
- Магазин на Laravel 7, часть 13. Панель управления, обрезка изображения и валидация данных
Поиск: ActiveForm • Web-разработка • Yii2 • База данных • Интернет магазин • Каталог товаров • Корзина • ActiveRecord • Заказ • Order • Форма • Basket