Practical symfony

Dia 3: O Modelo de Dados

You are currently browsing
the website for symfony 1

Visit the Symfony2 website


About

You are currently reading "Practical symfony" which is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported License license.

Jobeet Links

Master symfony

Be trained by SensioLabs experts (2 to 6 day sessions -- French or English).
trainings.sensiolabs.com

Books on symfony

Learn more about symfony with the official guides.
books.sensiolabs.com

L'audit Qualité par SensioLabs

200 points de contrôle de votre applicatif web.
audit.sensiolabs.com

Chapter Content

O Modelo Relacional

O Esquema

O Banco de Dados

O ORM

O Dados Iniciais

Veja em Ação no Navegador

Considerações finais

Feedback

symfony training
Be trained by symfony experts
May 29: Paris (Web Development with Symfony2 - Français)
May 31: Paris (Mastering Symfony2 - Français)
Jun 06: Paris (Introduction to Symfony2 - Français)
Jun 06: Paris (Introduction to Symfony2 - English)
Jun 06: Paris (Going Further with Symfony2 - English)
and more...

Search


powered by google
You are currently browsing "Practical symfony" in Brazilian Portuguese for the 1.4 version - Propel edition - Switch to version: - Switch to ORM: - Switch to language:
Creative Commons License This work is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.
Practical symfony (Propel edition)
Support symfony!
Buy this book
or donate.
Buy Practical symfony (Propel edition) from amazon.com

Aqueles de vocês que estão loucos para abrir o editor de texto e escrever um pouco de PHP ficarão felizes de saber que o dia de hoje nos levará a desenvolver alguma coisa. Iremos definir o modelo de dados do Jobeet, usar um ORM para interagir com o banco de dados e criar o primeiro módulo da aplicação. Apesar disso como o symfony faz um monte de trabalho para nós, teremos um módulo web totalmente funcional sem escrever muito código PHP.

O Modelo Relacional

As user stories que vimos ontem descrevem os objetos principais do nosso projeto: empregos, afiliados e categorias. Aqui temos o diagrama entidade-relacionamento correspondente (DER):

Entity relationship diagram

Além das colunas descritas nas stories, também adicionamos um campo created_at em algumas das tabelas. O symfony reconhece esses campos e define o valor dele para a hora atual do sistema quando um registro é criado. Foi feito o mesmo para os campos updated_at: os valores deles são definidos para a hora do sistema sempre que o registro for atualizado.

O Esquema

Para armazenar os empregos, afiliados e categorias, nós precisaremos com certeza de um banco de dados relacional.

Mas como o symfony é um framework orientado a objetos gostaríamos de manipular objetos onde pudermos. Por exemplo, em vez de escrever consultas SQL para recuperar registros do banco, iríamos preferir usar objetos.

A informação do banco de dados relacional precisar ser mapeada para um modelo de objeto. Isso pode ser feito com uma ferramenta de ORM e felizmente o symfony já vem com dois deles: Propel e Doctrine. Neste tutorial, nós iremos utilizar o Propel

O ORM precisa da descrição das tabelas e de seus relacionamentos para criar as classes relacionadas. Existem dois modos de criar esse esquema de descrição: analisando um tabela existente ou criando esse esquema manualmente.

Nota Alguma ferramentas permitem criar um banco de dados graficamente (por exemplo o Fabforce's Dbdesigner) e gerar diretamente um schema.xml (com o DB Designer 4 TO Propel Schema Converter).

Como o banco de dados não existe ainda e queremos manter o banco de dados do Jobeet agnóstico, vamos criar o arquivo schema manualmente editando o arquivo vazio config/schema.yml:

# config/schema.yml
propel:
  jobeet_category:
    id:           ~
    name:         { type: varchar(255), required: true, index: unique }
 
  jobeet_job:
    id:           ~
    category_id:  { type: integer, foreignTable: jobeet_category, foreignReference: id, required: true }
    type:         { type: varchar(255) }
    company:      { type: varchar(255), required: true }
    logo:         { type: varchar(255) }
    url:          { type: varchar(255) }
    position:     { type: varchar(255), required: true }
    location:     { type: varchar(255), required: true }
    description:  { type: longvarchar, required: true }
    how_to_apply: { type: longvarchar, required: true }
    token:        { type: varchar(255), required: true, index: unique }
    is_public:    { type: boolean, required: true, default: 1 }
    is_activated: { type: boolean, required: true, default: 0 }
    email:        { type: varchar(255), required: true }
    expires_at:   { type: timestamp, required: true }
    created_at:   ~
    updated_at:   ~
 
  jobeet_affiliate:
    id:           ~
    url:          { type: varchar(255), required: true }
    email:        { type: varchar(255), required: true, index: unique }
    token:        { type: varchar(255), required: true }
    is_active:    { type: boolean, required: true, default: 0 }
    created_at:   ~
 
  jobeet_category_affiliate:
    category_id:  { type: integer, foreignTable: jobeet_category, foreignReference: id, required: true, primaryKey: true, onDelete: cascade }
    affiliate_id: { type: integer, foreignTable: jobeet_affiliate, foreignReference: id, required: true, primaryKey: true, onDelete: cascade }

DICA Se você decidiu criar as tabelas escrevendo comandos SQL, você pode gerar o arquivo de configuração correspondente schema.yml rodando o comando propel:build-schema:

$ php symfony propel:build-schema

O comando acima requer que já tenha um banco de dados configurado em databases.yml. Mostraremos como fazer isso mais para frente. Se você tentar rodar agora esse comando ele não irá funcionar pois ele não saberá para qual banco ele vai ter que construir o esquema.

O esquema é a tradução direta do diagrama entidade-relacionamento para o formato YAML.

O arquivo schema.yml contém a descrição de todas as tabelas e suas colunas. Cada coluna é descrita com as seguintes informações:

Para colunas definidas como ~, que significam null em YAML (id, created_at e updated_at), o symfony tenta adivinhar a melhor configuração (chave primária no id e timestamp no created_at e update_at).

NOTA O atributo onDelete define a restrição de integridade ON DELETE para as chaves estrangeiras, e o Propel suporta CASCADE, SETNULL e RESTRICT. Por exemplo, quando um registro job é apagado, todos os registros jobeet_category_affiliate relacionados são automaticamente excluídos pelo banco de dados ou pelo Propel se o banco não suportar essa funcionalidade.

O Banco de Dados

O framework symfony suporta todos os bancos de dados suportados pelo PDO (MySQL, PostgreSQL, SQLite, Oracle, MSSQL, …). O PDO é a camada de abstração de banco de dados embutida no PHP.

Iremos usar o MySQL para esse tutorial:

$ mysqladmin -uroot -p create jobeet
Enter password: mYsEcret ## The password will echo as ********

Nota Sinta-se livre para escolher outra banco de dados se quiser. Não será difícil adaptar o código que vamos escrever pois iremos usar o ORM para escrever o SQL para nós.

Precisamos dizer ao symfony para usar esse banco de dados no nosso projeto Jobeet:

$ php symfony configure:database "mysql:host=localhost;dbname=jobeet" root mYsEcret

O comando configure:database recebe três argumentos: o DSN PDO, o nome de usuário e a senha para acessar o banco de dados. Se não for necessário usar uma senha para acessar o banco no servidor de desenvolvimento apenas omita o terceiro parâmetro.

NOTA O comando configure:database guarda a configuração do banco no arquivo config/databases.yml. Em vez de usar esse comando você também pode editar esse arquivo manualmente.

CUIDADO Passar a senha do banco de dados na linha de comando é conveniente mas também é inseguro|Security~. Dependendo de quem tem acesso ao seu ambiente pode ser melhor editar o arquivo config/databases.yml para mudar sua senha. É claro que para manter a senha segura, o modo de acesso arquivo de configuração deve ser restrito.

O ORM

Graças a descrição do banco de dados do arquivo schema.yml, podemos usar algumos comandos embutidos no Propel para gerar as instruções SQL necessárias para criar as tabelas do banco:

$ php symfony propel:build --sql

O comando propel:build --sql gera as instruções SQL no diretório data/sql/, otimizado para o servidor de banco de dados que configuramos:

# trecho do arquivo data/sql/lib.model.schema.sql
CREATE TABLE `jobeet_category`
(
        `id` INTEGER  NOT NULL AUTO_INCREMENT,
        `name` VARCHAR(255)  NOT NULL,
        PRIMARY KEY (`id`),
        UNIQUE KEY `jobeet_category_U_1` (`name`)
)Type=InnoDB;

Para criar realmente as tabelas no banco de dados, é necessário executar o comando propel:insert-sql:

$ php symfony propel:insert-sql

DICA Assim como qualquer ferramenta de linha de comando, os comandos do symfony podem receber argumentos e opções. Cada um dos comandos vem com uma mensagem de ajuda embutida que pode ser visualizada rodando o comando help:

$ php symfony help propel:insert-sql

A mensagem de ajuda lista todos os possíveis argumentos e opções, fornecendo os valores padrões de cada um e dando alguns exemplos úteis de uso.

O ORM também cria as classes PHP que mapeam os registros das tabelas em objetos:

$ php symfony propel:build --model

O comando propel:build --model cria arquivos PHP no diretório lib/model/ que pode ser usado para interagir com o banco de dados.

Se você navegar nos arquivos criados, provavelmente vai notar que o Propel cria quatro classes por tabela do banco. Para a tabela jobeet_job:

Os valores das colunas de um registro podem ser manipuladas com um objeto model usando alguns assessores (métodos get*()) e modificadores (métodos set*()):

$job = new JobeetJob();
$job->setPosition('Web developer');
$job->save();
 
echo $job->getPosition();
 
$job->delete();

Você também pode definir chaves estrangeiras diretamente ligando objetos:

$category = new JobeetCategory();
$category->setName('Programming');
 
$job = new JobeetJob();
$job->setCategory($category);

O comando propel:build --all é um atalho para os comandos que executamos nessa seção e um pouco mais. Então, execute esse comando agora para criar os formulários e validadores para as classes model do Jobeet.

$ php symfony propel:build --all --no-confirmation

Hoje você verá os validadores em ação e os formulários serão explicados em muitos detalhes no dia 10.

O Dados Iniciais

Ass tableas foram criadas no banco de dados mas ainda não tem nenhum dado nelas. Para qualquer aplicação web existem três tipos de dados:

Cada vez que o symfony cria as tabelas no banco de dados, todos os dados são perdidos. Para popular o banco com alguns dados iniciais, precisamos criar um script PHP ou executar alguns comandos SQL com o programa mysql. No entanto como essa é uma necessidade comum, há uma maneira melhor de fazer isso com o symfony: criar arquivos YAML no diretório data/fixtures/ e usar o comando propel:data-load para carregá-los no banco de dados.

Primeiro, crie os seguintes arquivos fixture:

# data/fixtures/010_categories.yml
JobeetCategory:
  design:        { name: Design }
  programming:   { name: Programming }
  manager:       { name: Manager }
  administrator: { name: Administrator }
 
# data/fixtures/020_jobs.yml
JobeetJob:
  job_sensio_labs:
    category_id:  programming
    type:         full-time
    company:      Sensio Labs
    logo:         sensio-labs.gif
    url:          http://www.sensiolabs.com/
    position:     Web Developer
    location:     Paris, France
    description:  |
      You've already developed websites with symfony and you want to
      work with Open-Source technologies. You have a minimum of 3
      years experience in web development with PHP or Java and you
      wish to participate to development of Web 2.0 sites using the
      best frameworks available.
    how_to_apply: |
      Send your resume to fabien.potencier [at] sensio.com
    is_public:    true
    is_activated: true
    token:        job_sensio_labs
    email:        job@example.com
    expires_at:   2010-10-10
 
  job_extreme_sensio:
    category_id:  design
    type:         part-time
    company:      Extreme Sensio
    logo:         extreme-sensio.gif
    url:          http://www.extreme-sensio.com/
    position:     Web Designer
    location:     Paris, France
    description:  |
      Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do
      eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
      enim ad minim veniam, quis nostrud exercitation ullamco laboris
      nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
      in reprehenderit in.
 
      Voluptate velit esse cillum dolore eu fugiat nulla pariatur.
      Excepteur sint occaecat cupidatat non proident, sunt in culpa
      qui officia deserunt mollit anim id est laborum.
    how_to_apply: |
      Send your resume to fabien.potencier [at] sensio.com
    is_public:    true
    is_activated: true
    token:        job_extreme_sensio
    email:        job@example.com
    expires_at:   2010-10-10

NOTA O arquivos fixture job referencia duas imagens. Você pode baixá-las (http://www.symfony-project.org/get/jobeet/sensio-labs.gif, http://www.symfony-project.org/get/jobeet/extreme-sensio.gif) e colocá-las dentro do diretório web/uploads/jobs/.

Um arquivo fixture é escrito em YAML, e define objetos model, rotulados com um nome único (por exemplo, definimos dois empregos rotulados como job_sensio_labs e job_extreme_sensio). Esse rótulo é de grande ajuda para ligar objetos relacionados sem precisar definir chaves primárias (que geralmente são auto-incrementadas e não poder ser ajustados). Por exemplo, a categoria do emprego job_sensio_labs é programming, que é o rótulo dado para a categoria 'Programming'.

DICA Em um arquivo YAML quando uma string contém quebras de linha (como a coluna description no arquivo fixture de empregos), você poder usar a barra vertical (|) para indicar que a string medirá várias linhas.

Embora um arquivo fixture pode conter objetos de um ou mais models, decidimos criar um arquivo para cada model com os fixtures do Jobeet.

DICA Observe os números no início dos nomes dos arquivos. Essa é a forma simples de controlar a ordem do carregamentos dos dados. Posteriormente no projeto, se precisarmos inserir novos arquivos fixture será fácil pois deixamos alguns números livres entre os já existentes.

Num arquivo fixture, você não precisa definir todos os valores das colunas. Nesse caso o symfony irá usar o valor padrão definido no esquema do banco de dados. E como o symfony utiliza o Propel para carregar os dados no banco, todos os comportamentos embutidos no ORM (como definir automaticamente as colunas created_at e updated_at) e os outros que personalizados que você possa ter adicionado ao model serão ativados.

Para carregar os dados iniciais no banco você deve simplesmente rodar o comando propel:data-load:

$ php symfony propel:data-load

DICA O comando propel:build --all --and-load é um atalho para o comando propel:build --all seguido pelo propel:data-load.

Veja em Ação no Navegador

Usamos bastante a interface de linha de comando mas isso não é realmente emocionante, especialmente para um projeto web. Agora nós temos tudo que precisamos para criar páginas web que interagem com o banco de dados.

Vamos dar uma olhada em como mostrar a lista de empregos, como editar um emprego existente e como excluir um emprego. Como explicado no primeiro dia, um projeto symfony é composto de aplicações. Cada aplicação é dividida em módulos. Um módulo é um conjunto independente de código PHP que representa uma funcionalidade da aplicação (por exemplo o módulo API), ou um conjunto de manipulações que o usuário pode fazer num objeto model (um módulo de empregos por exemplo).

O symfony é capaz de gerar automaticamente para cada um dos models um módulo que fornece funcionalidades básicas de manipulação:

$ php symfony propel:generate-module --with-show --non-verbose-templates frontend job JobeetJob

O comando propel:generate-module gera um módulo job na aplicação frontend para o model JobeetJob. Assim como com a maioria dos comandos symfony, são criados alguns arquivos e diretórios para você dentro do diretório apps/frontend/modules/job/:

Diretório Descrição
actions/ As actions do módulo
templates/ Os templates do módulo

O arquivo actions/actions.class.php define todos as actions disponíveis para o módulo job:

Nome da action Descrição
index Mostra os registros da tabela
show Mostra os campos e os valores de um determinado registro
new Mostra um formulário para criar um novo registro
create Cria um novo registro
edit Mostra um formulário para editar um registro existente
update Atualiza um register de acordo com os valores enviados pelo usuário
delete Exclui um determinado registro da tabela

Agora você pode testar o módulo job no navegador:

 http://www.jobeet.com.localhost/frontend_dev.php/job

Módulo Job

Se você tentar editar um emprego, você terá um exceção porque o symfony precisa de uma representação em texto da categoria. Uma representação de um objeto PHP pode ser definida com o método mágico do PHP __toString(). A representação do registro categoria deve ser definida na classe model JobeetCategory:

// lib/model/JobeetCategory.php
class JobeetCategory extends BaseJobeetCategory
{
  public function __toString()
  {
    return $this->getName();
  }
}

Agora cada vez que o symfony precisar de uma representação textual de uma categoria, ele chama o método __toString() que retorna o nome da categoria. Como iremos precisar de uma representação textual de todas nossas classes model um momento ou outro, vamos definir o método __toString() em todas as classes model:

// lib/model/JobeetJob.php
class JobeetJob extends BaseJobeetJob
{
  public function __toString()
  {
    return sprintf('%s at %s (%s)', $this->getPosition(), $this->getCompany(), $this->getLocation());
  }
}
 
// lib/model/JobeetAffiliate.php
class JobeetAffiliate extends BaseJobeetAffiliate
{
  public function __toString()
  {
    return $this->getUrl();
  }
}

Agora você pode criar e editar empregos. Tente deixar um campo obrigatório em branco, ou então digitar um data inválida. Isso mesmo, o symfony criou regras básicas de validação a partir da análise do esquema do banco de dados.

validação

Considerações finais

Isso é tudo. Já havíamos avisado na introdução. Hoje quase não escrevemos código PHP mas temos um módulo web para o model de empregos funcionando, pronto para ser refinado e personalizado. Lembre-se, nenhum código PHP também significa nenhum bug!

Se você ainda tiver alguma energia sobrando, fique à vontade para ler o código gerado automaticamente para o módulo e para o model e tente entender como ele funciona. Se não conseguir, não se preocupe e durma bem, pois amanhã iremos falar sobre um dos paradigmas mais utilizados nos frameworks web, o padrão de projeto MVC.

Feedback

Dica - pt_BR Este capítulo foi traduzido por Rogerio Prado de Jesus. Se encontrar algum erro que deseja corrigir ou quiser fazer algum comentário não deixe de enviar um e-mail para rogeriopradoj [at] gmail.com

Tip - en This chapter was translated by Rogerio Prado Jesus. If you find any errors to be corrected or you have any comments do not hesitate to send an email to rogeriopradoj [at] gmail.com

Dia 4: O Controller e a View »
« Dia 2: O projeto

Questions & Feedback

If you find a typo or an error, please register and open a ticket.

If you need support or have a technical question, please post to the official user mailing-list.