Сегодня рассмотрим создание сайта с нуля. Пусть это будет фотоальбом — с папками для снимков и, собственно, фотографиями.
В качестве платформы для хостинга используем Amazon Web Services.
Для разработки используем PHP-фреймворк Yii2. Выбор обоснован поддержкой MVC, высокой скоростью работы и минимальными потребностями в ресурсах.
Amazon Web Services
Подробности создания виртуального сервера на Amazon Web Services выходят за рамки статьи. Но основные шаги здесь перечислим:
- Создайте аккаунт.
- Создайте инстанс EC2 t2.micro — в течение года он будет доступен бесплатно, а ресурсов его будет достаточно для достаточно сложного сайта.
- По подсказкам создайте политику безопасности, получите приватный ключ и с помощью Putty или ssh подключитесь к созданному инстансу.
- Установите Apache 2.4, PHP 5.5 и MySQL (клиентскую и серверную часть).
Инсталляция Yii2
По умолчанию, Apache использует /var/www в качестве корневой папки сайта. Для простоты установим наше приложение в этой папке.
Рекомендуемым способом установки Yii2 является инсталляция через composer. В новом инстансе его нет, поэтому вначале установим:
curl -s http://getcomposer.org/installer | php mv composer.phar /usr/local/bin/composer
Теперь перейдем в корень сайта и скачаем Yii:
cd /var/www composer global require "fxp/composer-asset-plugin:1.0.0" composer create-project --prefer-dist yiisoft/yii2-app-basic gallery
В результате появится папка /var/www/gallery со всем необходимым.
Откройте в броузере адрес http://<внешний-IP-адрес-вашего-инстанса>/gallery/web/index.php — откроется шаблонное приложение yii2 при соблюдении условий:
- Apache запущен
- В политике безопасности отрыт доступ к 80-му порту
- Установка требуемых программных пакетов завершена успешно
Создание базы данных
Как говорилось выше, будем строить фотоальбом из снимков, объединяемых в папки.
Снимкам и папкам дадим название и описание.
Фотографии будем принимать в формате JPEG и хранить в одном каталоге. Для соблюдения уникальности названия файла, имена файлов будем менять на идентификатор записи о снимке.
Соответственно, для создания базы и таблиц используем команды:
CREATE DATABASE `gallery` /*!40100 DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci */; USE `gallery`; CREATE TABLE `folder` ( `id` int NOT NULL AUTO_INCREMENT PRIMARY KEY, `name` varchar(255) NOT NULL, `description` text NULL ) ENGINE='InnoDB' COLLATE 'utf8_unicode_ci'; CREATE TABLE `photo` ( `id` int NOT NULL AUTO_INCREMENT PRIMARY KEY, `name` varchar(255) NOT NULL, `description` text NULL, `folder_id` int(11) NOT NULL, FOREIGN KEY (`folder_id`) REFERENCES `folder` (`id`) ) ENGINE='InnoDB' COLLATE 'utf8_unicode_ci';
Примечание: если работать с MySQL с командной строки неудобно и непривычно, скачайте в корневую папку сайта adminer (это потребует предпринять дополнительные меры по обеспечению безопасности — как минимум, установите в MySQL пароль для пользователя root). Для текущей на данный момент версии 4.2.1 установка будет такой:
cd /var/www wget http://downloads.sourceforge.net/adminer/adminer-4.2.1.php
Для доступа к adminer в броузере введите http://<внешний-IP-адрес-вашего-инстанса>/adminer-4.2.1.php
Базу данных можно создать как командами, приведенными выше, так и при помощи интерфейса adminer. В результате должна получиться такая схема данных:
Создание моделей
Для начала настроим соединение с базой.
Откройте в текстовом редакторе файл /var/www/html/gallery/config/db.php и поменяйте название базы:
<?php return [ 'class' => 'yii\db\Connection', 'dsn' => 'mysql:host=localhost;dbname=gallery', 'username' => 'root', 'password' => '', 'charset' => 'utf8', ];
Теперь создаем модель для папки:
cd /var/www/html/gallery ./yii gii/model --tableName=folder --modelClass=Folder
На запрос подтверждения нажмите «Ввод» — утилита выполнит необходимые действия:
# ./yii gii/model --tableName=folder --modelClass=Folder Running 'Model Generator'... The following files will be generated: [new] models/Folder.php Ready to generate the selected files? (yes|no) [yes]: Files were generated successfully! Generating code using template "/var/www/html/gallery/vendor/yiisoft/yii2-gii/generators/model/default"... generated models/Folder.php done!
Аналогичным образом создайте модель для фотографии:
./yii gii/model --tableName=photo --modelClass=Photo
CRUD
Сокращение от Create, Read, Update, Delete — это набор операций, которые обеспечат функционирование сайта — создание, чтение, обновление и удаление папок и фотографий.
Начнем с папок:
./yii gii/crud --modelClass=app\\models\\Folder --controllerClass=app\\controllers\\FolderController
Как и при создании моделей, подтвердите создание файлов:
# ./yii gii/crud --modelClass=app\\models\\Folder --controllerClass=app\\controllers\\FolderController Running 'CRUD Generator'... The following files will be generated: [new] controllers/FolderController.php [new] views/folder/_form.php [new] views/folder/create.php [new] views/folder/index.php [new] views/folder/update.php [new] views/folder/view.php Ready to generate the selected files? (yes|no) [yes]: Files were generated successfully! Generating code using template "/var/www/html/gallery/vendor/yiisoft/yii2-gii/generators/crud/default"... generated controllers/FolderController.php generated views/folder/_form.php generated views/folder/create.php generated views/folder/index.php generated views/folder/update.php generated views/folder/view.php done!
Для проверки правильности создания файлов, откройте в броузере http://<внешний-IP-адрес-вашего-инстанса>/gallery/web/index.php?r=folder
Создайте несколько папок — позже мы используем их для загрузки фотографий:
Расширение модели photo
Как видно из схемы базы данных, в таблице фотографий сохраняются только названия и описания. Соответственно, созданная автоматически модель пока не имеет поле для загрузки файла.
Для добавления поля воспользуемся модулем, находящимся на стадии разработки. Для этого нам потребуется внести изменение в файл /var/www/html/gallery/composer.json:
"minimum-stability": "dev",
Теперь добавим модуль, отвечающий за поведение поля загрузки файла:
composer require --prefer-dist yii-dream-team/yii2-upload-behavior "*"
В качестве дополнения мы также получаем генератор миниатюр, что удобно при просмотре списка загруженных снимков.
Внесем следующие изменения в файл /var/www/html/gallery/models/Photo.php
<?php namespace app\models; use Yii; /** * This is the model class for table "photo". * * @property integer $id * @property string $name * @property string $description * @property integer $folder_id * * @property Folder $folder */ class Photo extends \yii\db\ActiveRecord { public $image; /** * @inheritdoc */ public static function tableName() { return 'photo'; } /** * @inheritdoc */ public function rules() { return [ [['name', 'folder_id'], 'required'], [['description'], 'string'], [['folder_id'], 'integer'], [['name'], 'string', 'max' => 255], [['image'], 'safe'], [['image'], 'file', 'extensions' => 'jpeg, jpg'], ]; } /** * @inheritdoc */ public function attributeLabels() { return [ 'id' => 'ID', 'name' => 'Name', 'description' => 'Description', 'folder_id' => 'Folder ID', ]; } /** * @return \yii\db\ActiveQuery */ public function getFolder() { return $this->hasOne(Folder::className(), ['id' => 'folder_id']); } public function behaviors() { return [ [ 'class' => '\yiidreamteam\upload\ImageUploadBehavior', 'attribute' => 'image', 'thumbs' => [ 'thumb' => ['width' => 200, 'height' => 150], ], 'filePath' => '@webroot/images/[[pk]].jpg', 'fileUrl' => '/images/[[pk]].jpg', 'thumbPath' => '@webroot/thumbs/[[profile]]_[[pk]].jpg', 'thumbUrl' => '/thumbs/[[profile]]_[[pk]].jpg', ], ]; } }
Добавим возможность создания, редактирования, просмотра и удаления фотографий:
./yii gii/crud --modelClass=app\\models\\Photo --controllerClass=app\\controllers\\PhotoController
Обновим форму загрузки/редактирования информации о фото в /var/www/html/gallery/views/photo/_form.php:
<?php use yii\helpers\Html; use yii\widgets\ActiveForm; use yii\helpers\ArrayHelper; use app\models\Folder; /* @var $this yii\web\View */ /* @var $model app\models\Photo */ /* @var $form yii\widgets\ActiveForm */ ?> <div class="photo-form"> <?php $form = ActiveForm::begin([ 'enableClientValidation' => false, 'options' => [ 'enctype' => 'multipart/form-data', ], ]); ?> <div class="form-group"> <?= Html::ActiveLabel($model, 'folder_id') ?> <?= Html::activeDropDownList($model, 'folder_id', ArrayHelper::map(Folder::find()->all(), 'id', 'name'), ['class'=>'form-control']) ?> </div> <?= $form->field($model, 'image')->fileInput() ?> <?= $form->field($model, 'name')->textInput(['maxlength' => 255]) ?> <?= $form->field($model, 'description')->textarea(['rows' => 6]) ?> <div class="form-group"> <?= Html::submitButton($model->isNewRecord ? 'Create' : 'Update', ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?> </div> <?php ActiveForm::end(); ?> </div>
Теперь — представление списка фотографий в /var/www/html/gallery/views/photo/index.php:
<?php use yii\helpers\Html; use yii\grid\GridView; /* @var $this yii\web\View */ /* @var $dataProvider yii\data\ActiveDataProvider */ $this->title = 'Photos'; $this->params['breadcrumbs'][] = $this->title; ?> <div class="photo-index"> <h1><?= Html::encode($this->title) ?></h1> <p> <?= Html::a('Create Photo', ['create'], ['class' => 'btn btn-success']) ?> </p> <?= GridView::widget([ 'dataProvider' => $dataProvider, 'columns' => [ ['class' => 'yii\grid\SerialColumn'], 'id', 'name', 'description:ntext', 'folder.name', array( 'format' => 'image', 'value' => function($data) { return 'thumbs/thumb_'.$data->id.'.jpg'; }, 'contentOptions' => ['class' => 'img-thumbnail'], ), ['class' => 'yii\grid\ActionColumn'], ], ]); ?> </div>
И просмотр отдельного фото в /var/www/html/gallery/views/photo/view.php:
<?php use yii\helpers\Html; use yii\widgets\DetailView; /* @var $this yii\web\View */ /* @var $model app\models\Photo */ $this->title = $model->name; $this->params['breadcrumbs'][] = ['label' => 'Photos', 'url' => ['index']]; $this->params['breadcrumbs'][] = $this->title; ?> <div class="photo-view"> <h1><?= Html::encode($this->title) ?></h1> <p> <?= Html::a('Update', ['update', 'id' => $model->id], ['class' => 'btn btn-primary']) ?> <?= Html::a('Delete', ['delete', 'id' => $model->id], [ 'class' => 'btn btn-danger', 'data' => [ 'confirm' => 'Are you sure you want to delete this item?', 'method' => 'post', ], ]) ?> </p> <?= DetailView::widget([ 'model' => $model, 'attributes' => [ 'id', 'name', 'description:ntext', 'folder.name', ], ]) ?> <?= Html::img('images/'.$model->id.'.jpg',[ 'class' => 'img-rounded', ]) ?> </div>
Для проверки логики загрузки и отображения снимков откройте в броузере http://<внешний-IP-адрес-вашего-инстанса>/gallery/web/index.php?r=photo и добавьте несколько снимков в подготовленные ранее папки.
Последние штрихи
Мы создали административную часть сайта. Теперь пришло время пользовательского интерфейса. Оставим эту работу нашим читателям — делитесь своими наработками. С удовольствием разместим ссылки на самые интересные репозитории и/или готовые сайты.
Что получилось?
Полнофункциональный сайт фотогалереи. Сайт хорошо смотрится как на компьютерах, так и на мобильных устройствах.