Статьи

Создайте опрос с помощью PHPixie

При выборе PHP-фреймворка вы должны убедиться, что он подчеркивает функции, которые наиболее важны для вас. Если вы ищете что-то быстрое, простое и легкое в освоении, то PHPixie может быть идеальным выбором.

Чтобы проиллюстрировать основы разработки с PHPixie, мы создадим небольшое приложение для опроса. К концу этого урока у вас будет работающее приложение для опроса и понимание того, как легко кодировать проекты с использованием PHPixie.

Прежде чем продолжить, убедитесь, что вы загрузили PHPixie с веб-сайта проекта и извлекли загруженный архив в корневой веб-каталог вашего веб-сервера. Посетите http: // localhost / (или другой соответствующий адрес) в вашем браузере, и вы должны увидеть «Развлекайся, кодируй!» приветственное сообщение.

Создание базы данных

Сначала нам нужно создать две таблицы в базе данных: polls будут хранить тему, по которой мы хотели бы получить мнения, а options будут содержать возможные ответы для каждого опроса и количество полученных голосов.

Откройте клиент MySQL и выполните следующие запросы:

 CREATE TABLE polls ( id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, topic VARCHAR(255) NOT NULL, PRIMARY KEY (id) ); CREATE TABLE options ( id INTEGER NOT NULL AUTO_INCREMENT, name VARCHAR(255) DEFAULT NULL, poll_id INTEGER UNSIGNED NOT NULL, votes INTEGER NOT NULL DEFAULT 0, PRIMARY KEY (id), FOREIGN KEY (poll_id) REFERENCES polls(id) ); 

Чтобы настроить соединение с базой данных, мы редактируем файл database.php каталоге application/config :

 <?php return array( 'default' => array( 'user' => 'dbuser', 'password' => 'dbpassword', 'driver' => 'pdo', // 'Connection' is required if we use the PDO driver 'connection' => 'mysql:host=localhost;dbname=polls', // 'db' and 'host' are required if we use Mysql driver 'db' => 'polls', 'host' => 'localhost' ) ); 

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

модели

Общий способ описания архитектуры Model-View-Controller заключается в следующем:

  • Модели представляют объекты данных, с которыми мы работаем (опросы и опции в нашем случае)
  • Контроллеры обрабатывают запросы пользователей и являются сердцем нашего приложения
  • Представления — это шаблоны, которые контроллеры используют для отображения ответа.

Как правило, модели обрабатывают такие операции, как получение данных из базы данных и их сохранение. PHPixie заботится об этих операциях, используя слой ORM, поэтому нам вообще не нужно писать запросы к базе данных. Нам просто нужно определить наши модели следующим образом:

 <?php class Poll_Model extends ORM { // Each poll can have many options public $has_many = array('options'); // This way we can define some additional // dynamic properties for a model. // Later on we will be able to access it via $poll->total_votes public function get($property) { if ($property == 'total_votes') { $total = 0; foreach ($this->options->find_all() as $option) { $total += $option->votes; } return $total; } } } 

Приведенный выше код должен быть помещен в application/classes/model/poll.php .

Отношения могут быть определены с использованием 3 массивов: belongs_to , has_one и has_many . Первый ( belongs_to ) определяет отношение один к одному, когда таблица ссылается на другую таблицу с помощью внешнего ключа, в нашем случае options принадлежат polls с использованием поля poll_id . Внешний ключ автоматически угадывается с использованием имен моделей PHPixie. С другой стороны, отношение принадлежит падению has_many , в нашем случае каждый опрос имеет много опций. has_one — это особый случай has_many , ограничивающий количество ссылочных объектов одним. Чтобы определить отношение, нам просто нужно добавить имя модели, которую мы хотим, в соответствующий массив. Возможна более тщательная конфигурация отношений, с возможностью указать, какое поле использовать для отношения, задать для него другое имя или создать отношение «многие ко многим» через таблицу ссылок, но в нашем случае, используя этот простой подход достаточно.

Метод find_all() ищет записи в базе данных. Когда мы ссылаемся на свойство options , ORM молча строит запрос к базе данных, чтобы выбрать все опции для текущего опроса. Вызов find_all() впоследствии выполняет этот запрос и возвращает эти параметры также в виде моделей ORM.

Переопределение метода get() позволяет нам динамически добавлять свойства в нашу модель. Если свойство, к которому мы пытаемся получить доступ, не найдено в классе модели, ORM вызовет метод get() класса, передав в качестве аргумента имя свойства. Любой результат, который get() , будет считаться значением для этого свойства и храниться внутри модели, поэтому, когда мы снова получим доступ к свойству, мы получим кэшированное значение. Поскольку внутренние операции модели обычно вызывают какие-либо действия с базой данных, предпочтительно кэшировать данные, поэтому мы переопределяем get() .

Обратите внимание, что хотя get() часто используется для кэширования данных из find_all() , она предназначена не только для этого. Например, мы можем использовать get() чтобы определить свойство, к которому мы будем обращаться несколько раз, но которое нужно получить только один раз.

Этот внутренний кеш сохраняется только для текущего объекта. Конечно, если вам вообще не нужно такое поведение, всегда можно добавить свой собственный total_votes() в модель, который будет пересчитывать голоса при каждом его вызове.

Код для модели параметров находится в application/classes/model/option.php :

 <?php class Option_Model extends ORM { public $belongs_to = array('poll'); public function get($property) { if ($property == 'percent') { if ($this->poll->total_votes == 0) { return 0; } return floor($this->votes / $this->poll->total_votes * 100); } } } 

Эта модель следует той же логике, что и предыдущая. Мы определяем отношение belongs_to с опросами и свойством percent , с той лишь разницей, что для отношений один к одному нет необходимости вызывать метод find_all() , поэтому мы можем получить доступ к опросу, которому принадлежит эта опция, как если бы это была собственность.

PHPixie в значительной степени опирается на соглашения об именах, чтобы сэкономить наше время при создании моделей, хотя это не заставляет нас называть все в соответствии с этими соглашениями (такое поведение легко изменить). Здесь мы следуем соглашению об использовании формы множественного числа при именовании таблиц в базе данных и единственной формы при именовании моделей.

Основной контроллер

URL для конкретного действия по умолчанию является / / / / / / / / / например, http: // localhost / polls / add . Также возможно передать дополнительный параметр ID после действия, которое мы будем использовать для доступа к отдельным опросам.

Наш контроллер должен знать, как отображать три типа страниц:

  • Главная страница со всеми перечисленными опросами
  • Единственная страница опроса, где мы можем увидеть текущие результаты и проголосовать
  • Форма для добавления нового опроса

Для каждой страницы должно быть определено действие внутри контроллера, и нам также понадобится HTML-шаблон для каждой страницы. Итак, простой контроллер, который просто отображает шаблоны, будет выглядеть так:

 <?php class Polls_Controller extends Controller { public function action_index() { // This is how we load up a template $view = View::get('index'); $this->response->body = $view->render(); } public function action_poll() { $view = View::get('poll'); $this->response->body = $view->render(); } public function action_add() { $view = View::get('add'); $this->response->body = $view->render(); } } 

Большая проблема с этой настройкой заключается в том, что внутри шаблонов будет повторяться много кода, потому что каждой странице нужен верхний и нижний колонтитулы. Решение состоит в том, чтобы создать один общий шаблон и затем включить в него подшаблоны. Мы можем сделать это с помощью методов before() и after() определенных внутри контроллера, и они будут выполняться соответственно до и после вызова действия. Теперь наш контроллер будет выглядеть следующим образом (сохраните следующее как application/classes/controller/polls.php ):

 <?php class Polls_Controller extends Controller { protected $view; public function before() { // We load up the main view and $this->view = View::get('main'); // Now we find a full path to a view that has // the same names as the action to be excuted $template = Misc::find_file('views', $this->request->param('action')); // We pass the view we located to our main template. // All properties assigned to a view will be available // as variables inside the template $this->view->template = $template; } public function after() { // After an action completes we render the view $this->response->body = $this->view->render(); } public function action_index(){ $view = View::get('index'); $this->response->body = $view->render(); } public function action_poll() { $view = View::get('poll'); $this->response->body = $view->render(); } public function action_add() { $view = View::get('add'); $this->response->body = $view->render(); } } 

Мы можем передать любые переменные, которые нам нравятся, просто назначив их в качестве свойств. Теперь, прежде чем мы добавим больше кода в контроллер, давайте посмотрим на сами представления.

HTML-макеты с использованием представлений

Представления — это в основном HTML-файлы со вставленным PHP-кодом для отображения значений, назначенных переменным-заполнителям изнутри контроллера. Наш основной вид довольно мал, поскольку все, что ему нужно сделать, это представить некоторый общий HTML-код и включить правильный подшаблон. Ниже приведен application/views/main.php :

 <!DOCTYPE html> <html> <head> <title>PHPixie polls</title> <link href="https://netdna.bootstrapcdn.com/twitter-bootstrap/2.2.2/css/bootstrap-combined.min.css" rel="stylesheet"> <link href="/style.css" rel="stylesheet"> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script> </head> <body> <div class="container"> <div class="span4"></div> <div class="span4"> <h2>PHPixie Polls</h2> <!-- Here is where we include a subtemplate --> <?php include($template);?> </div> <div class="span4"></div> </div> </body> </html> 

Мы будем использовать Bootstrap в качестве основы для наших стилей страниц, поэтому нам не нужно слишком концентрироваться на дизайне. Единственные изменения, которые нам нужно внести в CSS, находятся в файле web/style.css .

 .nav .muted { float: right; } td form { margin-bottom: 0px; } .filled { background: #08C; height: 20px; } .bar { width: 100px; } 

Теперь мы можем создавать отдельные страницы.

Создание отдельных страниц

Страница опросов должна просто представить список доступных опросов. Требуется лишь небольшое изменение в controller/polls.php .

 <?php //... public function action_index() { // We pass all stored polls to the view $this->view->polls = ORM::factory('poll')->find_all(); } 

Представление для страницы будет простым списком со ссылкой на страницу создания опроса. Ниже приведен код для views/index.php :

 <ul class="nav nav-tabs nav-stacked"> <?php foreach($polls as $poll) { ?> <li> <!-- This is how a link to a single poll will look like --> <a href="/polls/poll/<?=$poll->id;?>"><?=$poll->topic;?> <div class="muted"><?=$poll->total_votes; ?> Votes</div> </a> </li> <?php } ?> </ul> <a class="btn btn-block" href="/polls/add"><i class="icon-plus"></i> Add a Poll</a> 

Если бы мы добавили некоторые данные в базу данных на этом этапе, мы бы увидели следующее при доступе к http: // localhost / polls / index ( индекс может быть опущен из URL, поскольку это действие по умолчанию):

phpixie-01

Создание опросов требует немного больше работы. Мы должны проверить, была ли отправлена ​​форма, если это так, мы сохраняем опрос и перенаправляем пользователя обратно в список, в противном случае мы отображаем форму. Добавьте следующее обновление в controller/polls.php :

 <?php //... public function action_add() { // We only need to perform this if the form was submitted if ($this->request->method == 'POST') { // Creating a new poll $poll = ORM::factory('poll'); // This is how you access POST form data $poll->topic = $this->request->post('topic'); // Save the poll to the database $poll->save(); // Similarly we create and save options specified for this poll foreach($this->request->post('options') as $name) { if (empty($name)) { continue; } $option = ORM::factory('option'); $option->name = $name; $option->save(); // We add each option to the 'options' relation we defined for the poll $poll->add('options', $option); } // This will prevent after() from executing // we need to do this because there is no point of returning any data // if we redirect the user $this->execute=false; $this->response->redirect('/polls/'); return; } // If the form was not submitted the after() method will // take care of showing the creation form } 

В шаблоне для создания опроса нет ничего особенного. Используйте следующее для создания views/add.php :

 <script> $(function() { $('#addOption').click(function(evt) { evt.preventDefault(); var newOption = $('.option:eq(0)').clone(); newOption.find('input').val('').attr('placeholder', 'Option #'+($('.option').length + 1)); $('#options').append(newOption); }) }) </script> <form method="POST"> <fieldset> <legend>Add a new poll</legend> <label>Topic</label> <input type="text" placeholder="Type your topic here..." name="topic"> <label>Options</label> <div id="options"> <div class="option"> <input type="text" name="options[]" placeholder="Option #1"> </div> <div class="option"> <input type="text" name="options[]" placeholder="Option #2"> </div> <div class="option"> <input type="text" name="options[]" placeholder="Option #3"> </div> </div> <button class="btn" id="addOption"><i class="icon-plus"></i> Add option</button> <button class="btn btn-primary"><i class="icon-ok icon-white"></i> Save poll</button> </fieldset> </form> <a class="btn btn-link" href="/polls">&lt; Back to polls</a> 

Обратите внимание, что мы указали имя для ввода текста options[] например options[] , таким образом, оно будет передано в виде массива опций в PHP. Мы также добавили удобную кнопку «Добавить параметр» для добавления дополнительных строк. Результат должен выглядеть следующим образом:

phpixie-02

Я надеюсь, что на данный момент вы уже можете предсказать часть кода, который вы увидите здесь. Этот контроллер должен получить опрос, указанный идентификатором в URL-адресе, и он также должен обрабатывать пользователей, голосующих за опцию. Снова внесите следующие изменения в controller/polls.php :

 <?php //... public function action_poll() { // Handle voting if ($this->request->method == 'POST') { // If an option is was supplied via POST we increment // its votes field by 1 $option_id = $this->request->post('option'); $option = ORM::factory('option')->where('id', $option_id)->find(); $option->votes += 1; $option->save(); // Now we redirect the user back to current polls' page // This is done so that refreshing a browser window will not // produce multiple votes $this->response->redirect('/polls/poll/' . $option->poll->id); $this->execute = false; return; } // You can get the url id parameter using param() $id = $this->request->param('id'); $this->view->poll = ORM::factory('poll')->where('id', $id)->find(); } 

Метод find() действует так же, как find_all() но возвращает только первый результат. Внутри представления мы хотим нарисовать столбцы, которые отображали бы относительное количество голосов, и именно поэтому мы добавили это свойство процента в модель опционов ранее. Следующий код предназначен для views/poll.php :

 <h3><?=$poll->topic;?></h3> <table class="table"> <?php foreach ($poll->options->find_all() as $option) { ?> <tr> <td><?=$option->name;?></td> <td><?=$option->votes;?></td> <td class="bar"> <div class="filled" style="width:<?=$option->percent;?>%;"></div> </td> <td> <form method="POST"> <input type="hidden" name="option" value="<?=$option->id;?>"> <button class="btn btn-mini">Vote</button> </form> </td> </tr> <?php } ?> </table> <a class="btn btn-link" href="/polls">&lt; Back to polls</a> 

И вот как это будет выглядеть:

phpixie-03

Почти сделано

Прежде чем мы объявим наше приложение завершенным, мы должны убедиться, что оно доступно, посетив http: // localhost / . Для этого нам нужно изменить контроллер по умолчанию в application/config/core.php конфигурации application/config/core.php с «home» на «polls», например так:

 <?php return array( 'routes' => array( array('default', '(/<controller>(/<action>(/<id>)))', array( 'controller' => 'polls', 'action' => 'index' ) ) ), 'modules' => array('database', 'orm') ); 

Теперь у нас есть полнофункциональное приложение для опроса, а также понимание того, как PHPixie работает, и насколько легко с ним работать. Если вы хотите скачать код этой статьи для изучения, вы можете найти его на GitHub.

Тем не менее, еще есть чему поучиться. Такие вещи, как пользовательские маршруты и сложные отношения ORM, могут помочь вам еще больше адаптировать ваш проект. Узнайте больше о них, посетив сайт PHPixie.

Изображение через Fotolia