Первым делом мы определим модель данных для Joboard, а для взаимодействия с базой данных будем использовать ORM и в конце этой статьи у вас будет создан первый модуль приложения. Но так как Symfony делает много работы за нас, то этот полностью функциональный веб-модуль мы создадим без написания большого количество кода на PHP.
Реляционная модель
Пользовательские истории в предыдущей части, описывают основные объекты нашего проекта: вакансии, партнёров и категории. Вот диаграмма отношений соответствующих сущностей:
В дополнение к полям описанных в пользовательских историях, мы также добавим столбцы created_at
и updated_at
. Symfony будет автоматически устанавливать их значение при сохранении или обновлении объекта.
База данных
Для сохранения вакансий, партнёров и категорий в базе данных, Symfony 2 по умолчанию использует библиотеку Doctrine ORM. Чтобы определить параметры подключения к базе данных, вы должны отредактировать файл app/config/parameters.yml (для этого руководства мы будем использовать MySQL):
parameters:
database_driver: pdo_mysql
database_host: localhost
database_port: null
database_name: joboard
database_user: root
database_password: password
# ...
Теперь, когда Doctrine знает о вашей базе данных, вы можете создать её с помощью консольной команды, откройте терминал перейдите в директорию вашего проекта и выполните:
php app/console doctrine:database:create
Схема
Чтобы рассказать Doctrine о наших объектах, мы создадим файлы «метаданных», в которых описывается то, как объекты будут храниться в базе данных. Теперь перейдите в редактор кода и внутри каталога src/App/JoboardBundle/Resources/config
создайте каталог doctrine (должно получиться — src/App/JoboardBundle/Resources/config/doctrine
).
Директория doctrine будет содержать три файла: Category.orm.yml, Job.orm.yml и Affiliate.orm.yml.
Category.orm.yml
App\JoboardBundle\Entity\Category:
type: entity
repositoryClass: App\JoboardBundle\Repository\CategoryRepository
table: category
id:
id:
type: integer
generator: { strategy: AUTO }
fields:
name:
type: string
length: 255
unique: true
oneToMany:
jobs:
targetEntity: Job
mappedBy: category
manyToMany:
affiliates:
targetEntity: Affiliate
mappedBy: categories
Job.orm.yml
App\JoboardBundle\Entity\Job:
type: entity
repositoryClass: App\JoboardBundle\Repository\JobRepository
table: job
id:
id:
type: integer
generator: { strategy: AUTO }
fields:
type:
type: string
length: 255
nullable: true
company:
type: string
length: 255
logo:
type: string
length: 255
nullable: true
url:
type: string
length: 255
nullable: true
position:
type: string
length: 255
location:
type: string
length: 255
description:
type: text
how_to_apply:
type: text
token:
type: string
length: 255
unique: true
is_public:
type: boolean
nullable: true
is_activated:
type: boolean
nullable: true
email:
type: string
length: 255
expires_at:
type: datetime
created_at:
type: datetime
updated_at:
type: datetime
nullable: true
manyToOne:
category:
targetEntity: Category
inversedBy: jobs
joinColumn:
name: category_id
referencedColumnName: id
lifecycleCallbacks:
prePersist: [setCreatedAtValue]
preUpdate: [setUpdatedAtValue]
Affiliate.orm.yml
App\JoboardBundle\Entity\Affiliate:
type: entity
repositoryClass: App\JoboardBundle\Repository\AffiliateRepository
table: affiliate
id:
id:
type: integer
generator: { strategy: AUTO }
fields:
url:
type: string
length: 255
email:
type: string
length: 255
unique: true
token:
type: string
length: 255
is_active:
type: boolean
nullable: true
created_at:
type: datetime
manyToMany:
categories:
targetEntity: Category
inversedBy: affiliates
joinTable:
name: category_affiliate
joinColumns:
affiliate_id:
referencedColumnName: id
inverseJoinColumns:
category_id:
referencedColumnName: id
lifecycleCallbacks:
prePersist: [setCreatedAtValue]
ORM
Теперь Doctrine может создавать классы моделей (entities). Выполните в терминале команду:
php app/console doctrine:generate:entities AppJoboardBundle
Если вы посмотрите в каталог Entity
в JoboardBundle, то найдёте там, только что созданные классы: Category.php, Job.php и Affiliate.php. Откройте Job.php и задайте значения created_at
и updated_at
, как показано ниже:
<?php
// ...
/**
* @ORM\PrePersist
*/
public function setCreatedAtValue()
{
if(!$this->getCreatedAt())
{
$this->created_at = new \DateTime();
}
}
/**
* @ORM\PreUpdate
*/
public function setUpdatedAtValue()
{
$this->updated_at = new \DateTime();
}
Сделайте то же самое для значения created_at
класса партнёра (Affiliate.php):
<?php
// ...
/**
* @ORM\PrePersist
*/
public function setCreatedAtValue()
{
$this->created_at = new \DateTime();
}
// ...
Docrine будет автоматически обновлять эти поля при сохранении или обновлении записи. Это поведение было определено в файлах Affiliate.orm.yml и Job.orm.yml, с помощью раздела lifecycleCallbacks
и значения prePersist
: [setCreatedAtValue]
.
Мы также попросим у Doctrine, чтобы она создала таблицы в базе данных с помощью следующей команды:
php app/console doctrine:schema:update --force
Такой способ создания таблиц должен использоваться только во время разработки. Для более надежного метода систематического обновления рабочей базы данных, читайте о миграциях.
Мы создали таблицы в базе данных, но в них нет данных. Для любого веб-приложения, существует три типа данных: предварительные данные (это необходимо для базовой работы приложения, в нашем случае у нас будут некоторые первоначальные категории и администратор), тестовые данные (необходимо для тестирования приложения) и пользовательские данные (созданные пользователями во время нормальной жизни приложения).
Для заполнения базы данных начальными данными (начальные данные называются — фикстуры), мы будем использовать DoctrineFixturesBundle. Чтобы настроить этот пакет, мы должны следовать следующим шагам:
Добавьте следующий текст в файл composer.json, в разделе require
:
// ...
"require": {
// ...
"doctrine/doctrine-fixtures-bundle": "dev-master",
"doctrine/data-fixtures": "dev-master"
},
// ...
Обновите библиотеки:
php composer.phar update
Зарегистрируйте пакет DoctrineFixturesBundle в app/AppKernel.php
:
<?php
// ...
public function registerBundles()
{
$bundles = array(
// ...
new Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle()
);
// ...
}
Теперь, когда все установлено, в JoboardBundle нужно создать новую директорию с именем DataFixtures/ORM
, там мы создадим несколько новых классов для загрузки начальных данных LoadCategoryData.php
и LoadJobData.php
(файлы должны называться также как и классы):
LoadCategoryData.php
<?php
# src/App/JoboardBundle/DataFixtures/ORM/LoadCategoryData.php
namespace App\JoboardBundle\DataFixtures\ORM;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use App\JoboardBundle\Entity\Category;
class LoadCategoryData extends AbstractFixture implements OrderedFixtureInterface
{
public function load(ObjectManager $em)
{
$design = new Category();
$design->setName('Дизайн');
$programming = new Category();
$programming->setName('Программирование');
$manager = new Category();
$manager->setName('Менеджмент');
$administrator = new Category();
$administrator->setName('Администрирование');
$em->persist($design);
$em->persist($programming);
$em->persist($manager);
$em->persist($administrator);
$em->flush();
$this->addReference('category-design', $design);
$this->addReference('category-programming', $programming);
$this->addReference('category-manager', $manager);
$this->addReference('category-administrator', $administrator);
}
public function getOrder()
{
return 1;
}
}
LoadJobData.php
<?php
# src/App/JoboardBundle/DataFixtures/ORM/LoadJobData
namespace App\JoboardBundle\DataFixtures\ORM;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use App\JoboardBundle\Entity\Job;
class LoadJobData extends AbstractFixture implements OrderedFixtureInterface
{
public function load(ObjectManager $em)
{
$jobFullTime = new Job();
$jobFullTime->setCategory($em->merge($this->getReference('category-programming')));
$jobFullTime->setType('full-time');
$jobFullTime->setCompany('ООО Компания');
$jobFullTime->setLogo('company_logo.png');
$jobFullTime->setUrl('http://example.com/');
$jobFullTime->setPosition('Web Разработчик');
$jobFullTime->setLocation('Москва');
$jobFullTime->setDescription('Нужен опытный PHP разработчик');
$jobFullTime->setHowToApply('Высылайте резюме на [email protected]');
$jobFullTime->setIsPublic(true);
$jobFullTime->setIsActivated(true);
$jobFullTime->setToken('job_example_com');
$jobFullTime->setEmail('[email protected]');
$jobFullTime->setExpiresAt(new \DateTime('+30 days'));
$jobPartTime = new Job();
$jobPartTime->setCategory($em->merge($this->getReference('category-design')));
$jobPartTime->setType('part-time');
$jobPartTime->setCompany('ООО Дизайн Компания');
$jobPartTime->setLogo('design_company_logo.gif');
$jobPartTime->setUrl('http://design.example.com/');
$jobPartTime->setPosition('Web Дизайнер');
$jobPartTime->setLocation('Москва');
$jobPartTime->setDescription('Ищем профессионального дизайнера');
$jobPartTime->setHowToApply('Высылайте резюме на [email protected]');
$jobPartTime->setIsPublic(true);
$jobPartTime->setIsActivated(true);
$jobPartTime->setToken('[email protected]');
$jobPartTime->setEmail('[email protected]');
$jobPartTime->setExpiresAt(new \DateTime('+30 days'));
$em->persist($jobFullTime);
$em->persist($jobPartTime);
$em->flush();
}
public function getOrder()
{
return 2;
}
}
После того, как были написаны фикстуры, вы можете загрузить их через командную строку с помощью команды doctrine:fixtures:load
:
php app/console doctrine:fixtures:load
Теперь если вы проверите базу данных, то в таблицах вы должны увидеть загруженные из фикстур данные.
Смотрим на данные в браузере
Если вы запустите следующую команду, то она создаст новый контроллер src/App/JoboardBundle/Controller/JobController.php
с действиями для просмотра, создания, редактирования и удаление вакансий (и их соответствующие шаблоны, формы и маршруты):
php app/console doctrine:generate:crud --entity=AppJoboardBundle:Job --route-prefix=app_job --with-write --format=yml
Во время выполнения этой команды, вам нужно будет указать некоторые параметры, можете просто выбрать значения по умолчанию (которые в квадратных скобках). Для просмотра списка вакансий в браузере, мы должны импортировать новые маршруты, которые были созданы в src/App/JoboardBundle/Resources/config/routing/job.yml
в файл маршрутизации основного бандла, добавьте следующий код в основной файл маршрутизации src/App/JoboardBundle/Resources/config/routing.yml
:
AppJoboardBundle_job:
resource: "@AppJoboardBundle/Resources/config/routing/job.yml"
prefix: /job
Нам также нужно будет добавить метод _toString()
в класс категории src\App\JoboardBundle\Entity\Category.php, чтобы модель Category могла использоваться в выпадающем списке в форме редактирования вакансии:
<?php
// ...
public function __toString()
{
return $this->getName() ? $this->getName() : "";
}
// ...
Очистите кэш:
php app/console cache:clear --env=dev
php app/console cache:clear --env=prod
Теперь можно проверить работу контроллера в браузере: http://joboard.local/job/ или в окружении разработки, http://joboard.local/app_dev.php/job/.
Теперь можно создавать и редактировать вакансии. Попробуйте оставить обязательные поля незаполненными или попробуйте ввести недопустимые данные. Это верно, Symfony создает основные правила проверки просмотрев схему базы данных. Вот и все. Сегодня мы написали мало PHP кода, но у нас уже есть рабочий модуль для модели вакансий. В следующей части мы будем работать с контроллером и представлением. Увидимся в следующей части!