novidades-symfony
novidades-symfony
Symfony Brasil
26 posts
Novidades sobre o Framework Symfony2
Don't wanna be here? Send us removal request.
novidades-symfony · 9 years ago
Text
Novidade no Symfony 3.2: Constantes PHP nos arquivos YAML
YAML é, sem dúvida, o formato mais popular para definir as configurações nas aplicações Symfony. Infelizmente, devido a natureza do formato YAML, ele não fornece paridade de recursos com outros formatos, como XML. Em particular, não é possível usar constantes do PHP em arquivos YAML, por exemplo, como argumentos de serviço.
No symfony 3.2, decidimos aprimorar o formato YAML com uma extensão personalizada para suportar constantes do PHP. Se algum conteúdo em um arquivo YAML for prefixado com a string !php/const:, ele é agora considerado uma constante PHP:
parameters:    # this is considered a regular string    foo: PHP_INT_MAX    # this is considered a PHP constant    bar: !php/const:PHP_INT_MAX
Por padrão, o componente Yaml do Symfony somente faz o parse/gera conteúdos compatíveis com o padrão YAML. Portanto, ao usar o componente Yaml stand-alone, é necessário ativar esse recurso de forma explícita com a flag Yaml::PARSE_CONSTANT:
use Symfony\Component\Yaml\Yaml; $yaml = <<<YAML foo:    !php/const:PHP_INT_MAX YAML; $config = Yaml::parse($yaml, Yaml::PARSE_CONSTANT); // $config = array('foo' => PHP_INT_MAX);
Se você usa o framework Symfony, essa opção é ativada por padrão no YamlFileLoader usado pelo componente DependencyInjection. Isso significa que você pode usar constantes do PHP em seus serviços YAML sem precisar configurar nada:
# app/config/services.yml services:    app.my_service:        # ...        arguments:            - '@app.other_service'            - !php/const:AppBundle\Entity\BlogPost::MUM_ITEMS            - !php/const:Symfony\Component\HttpKernel\Kernel::VERSION
Tradução de: “New in Symfony 3.2: PHP constants in YAML files por Javier Eguiluz”
1 note · View note
novidades-symfony · 9 years ago
Text
Novidade no Symfony 3.2: Helper file no controlador
O controlador base do Symfony é uma classe utilitária que inclui vários métodos helper para tarefas comuns do controlador, e fornece acesso direto ao container de serviço. No Symfony 3.2, foi adicionado um novo método helper chamado file() para simplificar a tarefa de servir arquivos binários.
A forma mais simples de usar o helper file() é passar o caminho do arquivo para download. O navegador irá forçar o download desse arquivo e atribuirá a ele o mesmo nome do arquivo original:
use Symfony\Bundle\FrameworkBundle\Controller\Controller; class BookController extends Controller {    public function downloadAction()    {        $pdfPath = $this->getParameter('dir.downloads').'/sample.pdf';        return $this->file($pdfPath);    } }
Se você prefere definir um nome personalizado para o arquivo, utilize o segundo argumento opcional:
return $this->file($pdfPath, 'sample-of-my-book.pdf');
Se você prefere mostrar o conteúdo do arquivo no navegador em vez de forçar o seu download, use o terceiro argumento opcional:
use Symfony\Component\HttpFoundation\ResponseHeaderBag; return $this->file($pdfPath, 'sample.pdf', ResponseHeaderBag::DISPOSITION_INLINE);
Além de strings do caminho do arquivo, esse helper também aceita instâncias File e UploadedFile como seu primeiro argumento:
use Symfony\Component\HttpFoundation\File\File; $samplePdf = new File($this->getParameter('dir.downloads').'/sample.pdf'); return $this->file($samplePdf);
Tradução de: “New in Symfony 3.2: File controller helper por Javier Eguiluz”
1 note · View note
novidades-symfony · 10 years ago
Text
Novidade no Symfony 2.8: componente PropertyInfo
PHP é uma linguagem de programação fracamente tipada. Isso significa que você não tem que declarar o tipo das variáveis ​​(por exemplo, int, bool) e você pode armazenar diferentes tipos de dados (por exemplo, números e strings) na mesma variável.
Os desenvolvedores têm superado parcialmente essa restrição, graças a indução de tipo (type hints), phpDoc e, a partir do PHP 7, declarações de tipo de retorno. Entretanto, em alguns casos, ainda é necessário conhecer o tipo das propriedades PHP. É por isso que o Symfony adicionou um novo componente chamado PropertyInfo.
Em uma classe PHP, esse componente obtém informações sobre todas as suas propriedades pela introspecção de vários providers de metadados, como o mapeamento do ORM Doctrine, comentários PHPDoc, indução de tipo (type hints) do PHP, metadados do serializer, etc.
Considere a seguinte classe PHP de uma entidade do Doctrine, que contém diferentes tipos de propriedades:
namespace AppBundle\Entity; use Doctrine\ORM\Mapping as ORM; /** * @Entity */ class FooClass {    /**     * @ORM\Id     * @ORM\Column(type="integer")     */    public $id;    /**     * This is a date (short description).     *     * With a long description.     *     * @var \DateTime     */    public $foo;    private $bar;    public function setBar(\SplFileInfo $bar)    {        $this->bar = $bar;    } }
Antes de usar o componente PropertyInfo para extrair informações, você deve criar e configurar os extratores de metadados para usar (ReflectionExtractor, PhpDocExtractor, DoctrineExtractor e SerializerExtractor são embutidos, mas você pode criar os seus próprios extratores também):
use Symfony\Component\PropertyInfo\PropertyInfo; $propertyInfo = new PropertyInfo(    array($reflectionExtractor),    array($doctrineExtractor, $phpDocExtractor, $reflectionExtractor),    array($phpDocExtractor),    array($reflectionExtractor) );
Agora você pode fazer a introspecção de cada tipo de propriedade da seguinte forma:
$propertyInfo->getTypes('FooClass', 'foo'); // array(1) { //   [0] => //   class Symfony\Component\PropertyInfo\Type#36 (6) { //     private $builtinType => string(6) "object" //     private $nullable => bool(false) //     private $class => string(8) "DateTime" //     private $collection => bool(false) //     private $collectionKeyType => NULL //     private $collectionValueType => NULL //   } // } $propertyInfo->getTypes('FooClass', 'id'); // array(1) { //   [0] => //   class Symfony\Component\PropertyInfo\Type#36 (6) { //     private $builtinType => string(3) "int" //     private $nullable => bool(false) //     private $class => NULL //     private $collection => bool(false) //     private $collectionKeyType => NULL //     private $collectionValueType => NULL //   } // } $propertyInfo->getTypes('FooClass', 'bar'); // array(1) { //   [0] => //   class Symfony\Component\PropertyInfo\Type#245 (6) { //     private $builtinType => string(6) "object" //     private $nullable => bool(false) //     private $class => string(11) "SplFileInfo" //     private $collection => bool(false) //     private $collectionKeyType => NULL //     private $collectionValueType => NULL //   } // }
Esse componente inclui outros utilitários úteis para extrair informações:
$propertyInfo->getProperties('FooClass'); // ['id', 'foo', 'Bar'] $propertyInfo->isReadable('FooClass', 'id');   // true $propertyInfo->isReadable('FooClass', 'bar');  // false $propertyInfo->isWritable('FooClass', 'foo');  // true $propertyInfo->isWritable('FooClass', 'bar');  // true $propertyInfo->getShortDescription('FooClass', 'foo'); // "This is a date (short description)." $propertyInfo->getLongDescription('FooClass', 'foo'); // "With a long description."
Tradução de: “New in Symfony 2.8: PropertyInfo component porJavier Eguiluz”
1 note · View note
novidades-symfony · 10 years ago
Text
Novidade no Symfony 2.8: componente de autenticação Guard
O componente de Segurança do Symfony está dividido em duas partes principais: autenticação e autorização. O subsistema de autorização verifica se o usuário tem permissão para acessar o recurso informado. Esse sistema está relacionado aos papéis (roles) e voters e é poderoso e simples de usar.
O subsistema de autenticação verifica a identidade do usuário através de qualquer um dos métodos suportados: nome de usuário + senha, certificados, tokens de API, etc. Esse subsistema é poderoso e flexível, mas muitos desenvolvedores Symfony lutam com sua complexidade.
Um novo componente relacionado com a segurança chamado Guard visa simplificar o subsistema de autenticação. Essa nova abordagem radical tem como base a criação de apenas uma classe PHP que implementa GuardAuthenticatorInterface. Essa interface define os sete métodos a seguir:
interface GuardAuthenticatorInterface {    /**     * Get the authentication credentials from the request. If you return null,     * authentication will be skipped.     *     * For example, for a form login, you might:     *     *      return array(     *          'username' => $request->request->get('_username'),     *          'password' => $request->request->get('_password'),     *      );     *     * Or for an API token that's on a header, you might use:     *     *      return array('api_key' => $request->headers->get('X-API-TOKEN'));     */    public function getCredentials(Request $request);    /**     * Return a UserInterface object based on the credentials returned by getCredentials()     */    public function getUser($credentials, UserProviderInterface $userProvider);    /**     * Throw an AuthenticationException if the credentials returned by     * getCredentials() are invalid.     */    public function checkCredentials($credentials, UserInterface $user);    /**     * Create an authenticated token for the given user. You can skip this     * method by extending the AbstractGuardAuthenticator class from your     * authenticator.     */    public function createAuthenticatedToken(UserInterface $user, $providerKey);    /**     * Called when authentication executed, but failed (e.g. wrong username password).     */    public function onAuthenticationFailure(Request $request, AuthenticationException $exception);    /**     * Called when authentication executed and was successful (for example a     * RedirectResponse to the last page they visited)     */    public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey);    /**     * Does this method support remember me cookies?     */    public function supportsRememberMe(); }
Após implementar essa interface você poderá autenticar usuários através de formulários de login, Facebook, Twitter ou qualquer outro serviço que tenha como base OAuth, tokens de API, etc. Além disso, você poderá personalizar o comportamento de sucesso e falha muito facilmente.
Mostrar um exemplo completo do componente Guard em ação está além da finalidade deste artigo, mas você pode ler (e nos ajudar a terminar) a documentação em progresso do componente e seu tutorial oficial.
Tradução de: “New in Symfony 2.8: Guard authentication component por Javier Eguiluz”
0 notes
novidades-symfony · 10 years ago
Text
Novidade no Symfony 2.8: Profiler Redesenhado
Algumas semanas atrás, apresentamos a barra de ferramentas para depuração redesenhada para o Symfony 2.8. A nova barra de ferramentas foi bem recebida pela comunidade, mas tinha uma falha: ao clicar em qualquer painel da barra de ferramentas, você era redirecionado para o profiler antigo.
É por isso que no dia seguinte da apresentação da barra de ferramentas, começamos a trabalhar na modernização do look and feel do profiler para coincidir com a nova barra de ferramentas. Um mês mais tarde, e depois de ter alterado mais de 5.000 linhas de código, estamos orgulhosos de apresentar o Profiler Symfony redesenhado.
O processo de redesenho
Esse redesenho intimidante, como a maioria dos projetos de design, começou na prancheta de desenho. Preparamos dezenas de mockups e esboços no papel para repensar todos os elementos renderizados nas páginas do profiler:
Tumblr media
O próximo passo foi preparar o wireframe do layout no qual as páginas do profiler baseiam-se. Novamente, uma série de projetos foram testados e, eventualmente, decidimos utilizar o seguinte layout:
Tumblr media
A etapa final foi redesenhar todas as páginas do profiler. Em vez de apenas atualizar sua aparência, passamos mais de 100 horas no total repensando todo e qualquer um dos seus conteúdos.
Explicar o conjunto completo de mudanças levaria muito tempo, então preferimos mostrar quatro comparações rápidas do profiler antigo e novo:
Tumblr media Tumblr media Tumblr media Tumblr media
Confira o pull request original para saber todos os detalhes sobre o novo design.
Como você pode obter o novo profiler?
O novo profiler fará sua estréia no Symfony 2.8 e 3.0, que serão lançados em novembro. O redesenho também estará disponível nos próximos lançamentos do Silex. Enquanto isso, você terá que usar a dependência versão 2.8.*@dev em seu arquivo composer.json.
Se quiser testá-lo o mais rápido possível, crie um novo projeto Symfony executando o seguinte comando:
$ composer create-project symfony/framework-standard-edition new_profiler "2.8.*@dev"
Tradução de: “New in Symfony 2.8: Redesigned Profiler por Javier Eguiluz”
0 notes
novidades-symfony · 10 years ago
Text
Conheça o bundle: MobileDetectBundle
Os celulares estão tomando conta do mundo e, por extensão, a web. Além do crescimento do design web responsivo, os sites estão cada vez mais projetando versões específicas para dispositivos móveis.
Neste artigo vamos apresentar o MobileDetectBundle, que detecta dispositivos móveis e ajuda a redirecionar os usuários para a versão apropriada do seu site. Uma vez instalado, esta é a única configuração que você precisa para setar o cenário habitual de redirecionar os usuários de dispositivos móveis para a versão m.host do seu site:
# app/config/config.yml mobile_detect:    redirect:        mobile:            is_enabled: true            host: http://m.example.com            action: redirect        detect_tablet_as_mobile: true
Leia a documentação oficial desse bundle para aprender a configurar cenários de redirecionamento mais complexos.
Além de redirecionar usuários, esse bundle também oferece um serviço mobile_detector que permite detectar tipos de dispositivos, sistemas operacionais e até mesmo fornecedores:
$device = $this->get('mobile_detect.mobile_detector'); // basic device detection $device->isMobile(); $device->isTablet(); // fine-grained device detection $device->isIphone(); $device->isIpad(); $device->isSamsung(); // device operating system $device->isIOS(); $device->isAndroidOS();
Todos esses métodos também estão disponíveis em seus templates Twig através de funções equivalentes fornecidas pelo bundle:
{% extends is_mobile() ? 'mobile/layout.html.twig': 'layout.html.twig' %} {% if is_android_os() %}    Download our application from the store. {% endif %} {% if is_device('samsung') %}    Thinking about buying an iPhone? Check out our deals! {% endif %}
Sobre o autor
O MobileDetectBundle foi desenvolvido por Nikolay Ivlev, um desenvolvedor Symfony de Moscou (Rússia).
Nós estamos procurando o próximo bundle
Você conhece algum outro bundle Symfony útil que não é bem conhecido pela comunidade ainda? Conte-nos sobre ele nos comentários do post original ou envie um e-mail para [email protected] e vamos apresentá-lo no próximo artigo dessa série!
Tradução de: “Meet the bundle: MobileDetectBundle porJavier Eguiluz”
0 notes
novidades-symfony · 10 years ago
Text
Novidade no Symfony 2.7: Refatoração do tipo de campo de formulário Choice
O tipo de campo de formulário Choice é um dos tipos mais avançados definidos pelo Symfony. É também um dos mais importantes, porque muitos outros tipos herdam dele, como EntityType, CountryType e LocaleType.
No Symfony 2.7 esse tipo de formulário foi completamente refatorado para suportar a geração dinâmica de labels, valores, índices e atributos. Isso é possível agora graças às novas opções choice_label, choice_name, choice_value, choice_attr, group_by e choices_as_values.
Geração dinâmica de labels
Por padrão, as chaves do array choices são usadas ​​como labels, mas você pode passar um callable PHP para a opção choice_label para gerá-las de forma dinâmica:
$builder->add('attending', 'choice', array(    'choices' => array(        'yes' => true,        'no' => false,        'maybe' => null,    ),    'choices_as_values' => true,    'choice_label' => function ($allChoices, $currentChoiceKey) {        return 'form.choice.'.$currentChoiceKey;    }, ));
Em vez de um callable, você também pode passar o nome da propriedade para usar como label:
$builder->add('attending', 'choice', array(    'choices' => array(        Status::getInstance(Status::YES),        Status::getInstance(Status::NO),        Status::getInstance(Status::MAYBE),    ),    'choices_as_values' => true,    'choice_label' => 'displayName', ));
Geração dinâmica dos nomes e valores da opção
Use a opção choice_name para gerar o nome do formulário para cada opção e choice_value para gerar o seu valor string:
$builder->add('attending', 'choice', array(    'choices' => array(        'Yes' => true,        'No' => false,        'Maybe' => null,    ),    'choices_as_values' => true,    'choice_name' => function ($allChoices, $currentChoiceKey) {        // use the labels as names        return strtolower($currentChoiceKey);    },    'choice_value' => function ($allChoices, $currentChoiceKey) {        if (null === $currentChoiceKey) {            return 'null';        }        if (true === $currentChoiceKey) {            return 'true';        }        return 'false';    }, ));
Em vez de um callable, nessas opções você também pode usar o nome de uma propriedade.
Geração dinâmica dos atributos da opção
A choice_attr define todos os atributos HTML adicionais aplicados para cada opção. Essa opção permite definir valores utilizando um array simples, que é ideal para opções simples:
$builder->add('attending', 'choice', array(    'choices' => array(        'Yes' => true,        'No' => false,        'Maybe' => null,    ),    'choices_as_values' => true,    'choice_attr' => array(        'Maybe' => array('class' => 'text-muted'),    ), ));
Ao usar opções mais complexas, é melhor usar um callable:
$builder->add('attending', 'choice', array(    'choices' => array(        'Yes' => true,        'No' => false,        'Maybe' => null,    ),    'choices_as_values' => true,    'choice_attr' => function ($allChoices, $currentChoiceKey) {        if (null === $currentChoiceKey) {            return array('class' => 'text-muted');        }    }, ));
Mais uma vez, você pode passar para essa opção um caminho de propriedade, em vez de um array ou um callable.
Geração dinâmica de grupos de opções
Por padrão, as opções são agrupadas usando a mesma estrutura fornecida pela opção choices:
$builder->add('attending', 'choice', array(    'choices' => array(        'Decided' => array(            'Yes' => true,            'No' => false,        ),        'Undecided' => array(            'Maybe' => null,        ),    ),    'choices_as_values' => true, ));
O agrupamento de escolhas também podem ser gerado dinâmicamente usando um callback ou um caminho de propriedade com a opção group_by:
$builder->add('attending', 'choice', array(    'choices' => array(        'Yes' => true,        'No' => false,        'Maybe' => null,    ),    'choices_as_values' => true,    // grouping defined using a callback    'group_by' => function ($allChoices, $currentChoiceKey) {        if (null === $currentChoiceKey) {            return 'Undecided';        }        return 'Decided';    }, ));
Geração dinâmica de opções preferidas
Por último, a geração das opções preferidas pode ser definida na nova opção preferred_choices usando um array, um callback ou um caminho de propriedade:
$builder->add('attending', 'choice', array(    'choices' => array(        'Yes' => true,        'No' => false,        'Maybe' => null,    ),    'choices_as_values' => true,    // using an array to show the 'true' choice as preferred    'preferred_choices' => array(true),    // using a callable to show the 'true' choice as preferred    'preferred_choices' => function ($choice, $key) {        return true === $choice;    }, ));
Tradução de: “New in Symfony 2.7: Choice form type refactorization por Javier Eguiluz”
0 notes
novidades-symfony · 10 years ago
Text
Novidade no Symfony 2.7: Melhorias para aumentar a sua produtividade
O Symfony 2.7 inclui inúmeros ajustes de código e melhorias. Muitos deles são pequenas alterações que vão aumentar a sua produtividade no dia-a-dia. Neste artigo, apresentamos sete dessas pequenas, mas agradáveis ​​funcionalidades.
Adicionado um método de atalho getParameter() no controlador base
Obter o valor de um parâmetro do container dentro de um controlador é uma das frustrações mais comuns quando você começa a aprender Symfony. Você geralmente tenta primeiro usar o método get() sem sorte, e, em seguida, tenta usar o método inexistente getParameter(). Felizmente, o Symfony 2.7 irá adicionar um novo atalho getParameter():
use Symfony\Bundle\FrameworkBundle\Controller\Controller; class DefaultController extends Controller {    public function indexAction()    {        // Symfony 2.6        $value = $this->container->getParameter('param_name');        // Symfony 2.7        $value = $this->getParameter('param_name');        // ...    } }
Adicionado feedback sobre o status da versão do Symfony
Quem quer usar uma versão desatualizada do Symfony considerando os ótimos recursos introduzidos em cada novo lançamento? Felizmente, a partir do Symfony 2.7, será mais difícil executar uma versão desatualizada. A razão é que a barra de ferramentas de debug web irá incluir um feedback visual sobre o status da sua versão Symfony:
Tumblr media
Exibido o status HTTP nos resultados de pesquisa do profiler
Às vezes é difícil procurar por uma requisição específica entre todas as requisições armazenadas pelo profiler do Symfony. No Symfony 2.7 isso será um pouco mais fácil graças à nova coluna que exibe o status de cada requisição HTTP.
Tumblr media
Por favor, note que se você armazenar as informações do profiler em um banco de dados do tipo MySQL ou SQLite, você deve descartar a tabela existente ou adicionar a nova coluna status_code manualmente.
Adicionado suporte para parâmetros do container nas condições das rotas
No Symfony 2.7, as condições das rotas agora suportam o uso de parâmetros do container em suas expressões. Apenas envolva os nomes de parâmetros com caracteres % e o Symfony irá substituí-los por seus valores antes de avaliar a expressão:
# app/config/routing.yml contact:    path:     /contact    defaults: { _controller: AcmeDemoBundle:Main:contact }    condition: "request.headers.get('User-Agent') matches '%allowed_user_agents%'"
Por favor, note que essa alteração introduz uma pequena incompatibilidade em alguns casos. Se a sua expressão usa o operador módulo desta forma: foo%bar%2, o Symfony 2.6 irá fazer o parse dela como $foo % $bar % 2 e o Symfony 2.7 substituirá %bar% pelo valor do parâmetro do container bar ou vai gerar um erro se ele não existir.
Os comandos lint foram movidos para o namespace lint:
No passado movemos todos os comandos de depuração do Symfony para o namespace debug:, a fim de agrupar melhor os comandos por função. O Symfony 2.7 faz o mesmo com os comandos lint usados ​​para descobrir erros de sintaxe em arquivos YAML e templates Twig:
# Symfony 2.6 $ php app/console yaml:lint ... $ php app/console twig:lint ... # Symfony 2.7 (the old command names also work) $ php app/console lint:yaml ... $ php app/console lint:twig ...
Isso pode parecer uma mudança insignificante, mas o agrupamento dos comandos de depuração sob debug: e comandos lint sob lint: torna o framework mais consistente e isso é sempre uma coisa boa.
Adicionado suporte para lint de vários templates Twig
No Symfony 2.7 você pode passar qualquer número de arquivos e/ou diretórios para o comando lint:twig:
# Symfony 2.6 (one template or one directory) $ php app/console lint:twig app/Resources/views/base.html.twig  1/1 valid files $ php app/console lint:twig app/Resources/views/blog/  4/4 valid files # Symfony 2.7 (any number of files and/or directories) $ php app/console lint:twig app/Resources/views/base.html.twig app/Resources/views/blog/  5/5 valid files
Reiniciar automaticamente o servidor web embutido
Usar o servidor web embutido do PHP é cada vez mais comum durante o desenvolvimento de aplicações. Você provavelmente usou o comando server:run disponível desde o Symfony 2.2. No entanto, a partir do Symfony 2.6 você também pode utilizar os comandos server:start, server:status and server:stop.
O único inconveniente é que o comando server:start requer a extensão pcntl para ser executado. No Symfony 2.7, se você executar server:start e não tem a extensão pcntl, o Symfony irá executar automaticamente o comando server:run.
Seja um membro ativo da comunidade
Todas essas melhorias têm sido propostas e implementadas pela incrível Comunidade Symfony. Se você também quer se tornar um membro ativo da comunidade, leia os guias contribuindo com o Symfony e considere propor novas idéias, enviando pull requests e revisando o código enviado por outros desenvolvedores.
Tradução de: “New in Symfony 2.7: Productivity improvements por Javier Eguiluz”
0 notes
novidades-symfony · 10 years ago
Text
A nova Aplicação de Demonstração do Symfony
Estamos contentes de apresentar oficialmente a aplicação de Demonstração Symfony chamada Symfony Demo. Esse projeto é uma aplicação Symfony totalmente funcional desenvolvida como um recurso de aprendizagem. Com essa aplicação torna-se obsoleto o antigo AcmeDemoBundle e ela pode ser considerada a implementação de referência das Melhores Práticas Symfony.
Tecnicamente, a aplicação consiste de um mecanismo de blog com uma parte pública e uma privada:
Tumblr media
A parte privada é um backend CRUD simples desenvolvido a partir do zero:
Tumblr media
Cada página da aplicação inclui um botão Show Source, que exibe o controlador Symfony e o template Twig usados para renderizar a página atual:
Tumblr media
Além disso, o código-fonte da aplicação contém muitos comentários para ajudar você a entender melhor por que e como as coisas funcionam.
Como instalar a Aplicação de Demonstração do Symfony
Abra um console de comando e execute o seguinte comando em qualquer lugar no seu sistema:
$ symfony demo
Este comando utiliza o Instalador do Symfony para baixar e instalar a aplicação de demonstração. Se o comando demo não funcionar, atualize a sua versão antiga do instalador executando symfony self-update. Uma vez que a aplicação de demonstração foi baixada, use o servidor web embutido para executá-la:
$ cd symfony_demo/ $ php app/console server:run
A aplicação de demonstração Symfony é projetada para executar sem que você precise configurar nada nem executar qualquer outro comando. Alternativamente, você pode obter a aplicação de demonstração através do repositório symfony/symfony-demo.
Para que serve essa aplicação?
1) Aprender Symfony
Essa é a razão mais óbvia para utilizar essa aplicação de demonstração e foi o principal objetivo ao desenvolver ela.
2) Ensinar Symfony
Essa aplicação também pode ser um recurso pedagógico útil para treinar novos funcionários na empresa e para utilizar em oficinas durante eventos organizados pela comunidade Symfony.
3) Testar novas funcionalidades do Symfony
Você quer testar as funcionalidades introduzidas por uma nova versão Symfony sem alterar as suas próprias aplicações? Instale a aplicação de demonstração, atualize as dependências do Symfony e está feito!
Por exemplo, você pode testar as funcionalidades do Symfony 2.7 beta da seguinte forma:
$ symfony demo $ cd symfony_demo/ $ composer require symfony/symfony:2.7.x@beta
4) Benchmarks relativos
A aplicação de demonstração foi desenvolvida como um recurso de aprendizagem, logo você não pode usá-la em uma avaliação de desempenho. No entanto, ela pode ser utilizada para fazer benchmarks de desempenho relativos.
Instale uma nova aplicação de demonstração e faça um profile dela no Blackfire. Introduza algumas mudanças na aplicação e faça o profile novamente. Agora você pode comparar visualmente ambas as execuções e verificar o quanto o desempenho da aplicação melhorou ou degradou.
Tradução de: “Introducing the Symfony Demo application por Javier Eguiluz”
0 notes
novidades-symfony · 10 years ago
Text
Novidade no Symfony 2.7: o novo componente Asset
Antes do Symfony 2.7, a gestão dos assets web era feita no componente Templating. O design do subsistema sofria de alguns erros (é necessário o escopo da requisição porque ele dependia da instância Request) e limitações (por exemplo na gestão de URLs base seguras).
Para resolver esses problemas, o Symfony 2.7 introduz um novo componente chamado Asset. A gestão de assets web agora é desacoplada do componente Templating, o que permite reutilizá-lo fora do Symfony (por exemplo, no microframework Silex).
O novo componente Asset gerencia a geração de URL e versionamento de assets web, tais como folhas de estilo CSS, arquivos JavaScript e arquivos de imagem. Isso significa que seus templates tornam-se menos verbosos e sua aplicação é mais flexível, porque você pode alterar o local e a versão de seus assets apenas com pequenas alterações em um arquivo de configuração.
Alterações de Configuração
As antigas opções assets_* definidas na seção templating são agora definidas em assets e nos seus nomes não existe mais o prefixo assets. As opções anteriores estão agora obsoletas (elas vão desaparecer no Symfony 3.0), mas, por enquanto, o componente Asset atualiza elas automaticamente (você não tem que fazer nenhuma mudança na configuração da sua aplicação, se não desejar):
# Symfony 2.6 # app/config/config.yml framework:    templating:        assets_version: 'v5'        assets_version_format: '%%s?version=%%s'        assets_base_urls:            http: ['http://cdn.example.com']            ssl:  ['https://secure.example.com']        packages:            # ... # Symfony 2.7 # app/config/config.yml framework:    assets:        version: 'v5'        version_format: '%%s?version=%%s'        base_path: ~        base_urls: ['http://cdn.example.com', 'https://secure.example.com']        packages:            # ...
A única alteração relevante é introduzido pela opção base_urls, que não separa mais as URLs regulares (http) e seguras (ssl).
Alterações na função Template
No Symfony 2.7, a função asset() bem conhecida do Twig removeu os argumentos opcionais absolute e version:
{# Symfony 2.6 #} {{ asset('logo.png', absolute = true) }} {# Symfony 2.7 #} {{ absolute_url(asset('logo.png')) }} {# Symfony 2.6 #} {{ asset('logo.png', version = 'v5') }} {# Symfony 2.7 (do nothing, version is automatically appended) #} {{ asset('logo.png') }} {# use the asset_version() function if you need to output it manually #} {{ asset_version('logo.png') }}
Exemplos de Uso
Leia a documentação do componente Asset para saber mais sobre as funcionalidades desse novo componente. Você vai descobrir como implementar truques como os seguintes:
Definir atalhos para os assets dos bundles
# app/config/config.yml framework:    assets:        packages:            app:                base_path: /bundles/app/            img:                base_path: /bundles/app/images/
{{ asset('images/me.png', 'app') }} {# /bundles/app/images/me.png #} {{ asset('me.png', 'img') }} {# /bundles/app/images/me.png #}
Versionamento avançado de asset
# app/config/config.yml framework:    assets:        version: 'v5'        version_format: '%%s?version=%%s'        packages:            avatar:                base_path: /img/avatars            doc:                base_path: /docs/pdf                version_format: '%2$s/%1$s'
{{ asset(user.uuid ~ '.png', 'avatar') }} {# /img/avatars/1b0ae6a5-1c39-4b49-bcc1-2a787c8ec139.png?version=v5 #} {{ asset('contracts/signup.pdf', 'doc') }} {# /v5/docs/pdf/contracts/signup.pdf #}
CDNs contextuais
# app/config/config.yml framework:    assets:        base_urls:            - 'http://static1.example.com/images/'            - 'https://static2.example.com/images/'
{{ asset('logo.png') }} {# in a regular page: http://static1.example.com/images/logo.png #} {# in a secure page:  https://static2.example.com/images/logo.png #}
Tradução de: “ New in Symfony 2.7: the new Asset component porJavier Eguiluz”
0 notes
novidades-symfony · 10 years ago
Text
Novidade no Symfony 2.7: Melhorias na Segurança
Além das mudanças de alto impacto e grandes recursos, as novas versões do Symfony sempre adicionam ajustes e pequenas melhorias em toda a sua base de código. Neste artigo você vai aprender sobre três pequenas alterações relacionadas à segurança que irão melhorar a sua produtividade no dia-a-dia como desenvolvedor.
Adicionada uma representação string para os usuários
Alguns desenvolvedores adicionam um método mágico PHP __toString() nas suas entidades de usuários para definir a representação string do usuário. Isso permite utilizar type casting, como (string) $user na aplicação PHP e {{ user }} nos templates Twig.
No entanto, em testes funcionais é comum utilizar usuários in-memory para simplificar os testes. O problema é que a classe User do núcleo definida pelo Symfony não inclui o método _toString() e todas as conversões de tipo (type cast) falham.
No Symfony 2.7 decidimos adicionar um novo método na classe User do núcleo, a fim de definir sua representação string. O código desse método é tão simples como:
// src/Symfony/Component/Security/Core/User/User.php public function __toString() {    return $this->getUsername(); }
Melhorada a extensão Twig de logout
O Symfony acrescenta extensões Twig personalizadas no topo do Twig para integrar alguns componentes nos templates. Você provavelmente conhece e usa muitas dessas funções, filtros e tags, como render(), |trans e {% form_theme %}.
Uma das extensões menos conhecidas e utilizadas são as funções logout_path e logout_url, que geram a URL relativa ou absoluta apropriada para logout do firewall informado:
<a href="{{ logout_path('firewall_name') }}">Close session</a>
No Symfony 2.7, o nome do firewall é opcional. Se você não fornecê-lo, o Symfony usará automaticamente o firewall atual, seja qual ele for:
<a href="{{ logout_path() }}">Close session</a>
Essa pequena alteração também permite usar essa função em templates onde você não sabe o nome do firewall; por exemplo, nos templates de bundles públicos de terceiros.
Adicionado um comando para codificar senhas
O Symfony 2.7 introduz um novo comando chamado security:encode-password que permite codificar senhas para a classe de usuário informada:
Tumblr media
Qual é a finalidade desse comando? Primeiro, quando você usa o provedor do usuário in-memory, não é trivial codificar a senha antes de armazená-la no arquivo de configuração de segurança. Por exemplo, ao usar o encoder sha512, você tinha que executar o seguinte comando para obter a senha codificada:
$ php -r '$pass = "..."; $salt = "..."; $iterations=5000; $salted = $pass.$salt; $digest = hash("sha512", $salted, true); for($i=1; $i<$iterations; $i++) { $digest = hash("sha512", $digest.$salted, true); } echo base64_encode($digest);'
No Symfony 2.7 você só tem que executar o seguinte:
$ php app/console security:encode-password 'plain_password' 'AppBundle\Entity\User'
Por padrão o valor do salt utilizado pela senha é gerado automaticamente, mas você pode adicionar a opção --empty-salt para evitar adicioná-lo. No entanto, você não pode fornecer o valor do salt, porque isso é considerado uma prática de segurança ruim (por exemplo, no PHP7 a opção salt da função password_hash() está obsoleta).
Além do provedor do usuário in-memory, esse comando também pode ser útil durante o desenvolvimento da aplicação, no caso de você precisar verificar ou atualizar manualmente algumas senhas codificadas armazenadas no banco de dados.
Tradução de: “New in Symfony 2.7: Security Improvements porJavier Eguiluz”
0 notes
novidades-symfony · 10 years ago
Text
Novidade no Symfony 2.7: Profiler Twig
No projeto Symfony acreditamos que todas as informações relevantes para os desenvolvedores devem estar a um clique de distância. É por isso que no Symfony 2.7 adicionamos um novo painel com informações sobre o Twig na barra de ferramentas de debug.
Atualmente, as informações sobre os recursos utilizados para renderizar cada template são exibidas no painel Timeline do profiler Symfony:
Tumblr media
Infelizmente, esse painel não fornece muitos detalhes sobre os templates. Além disso, alguns templates não estão registados, logo, sua informação não está disponível nesse painel.
O Symfony 2.7 resolve esses problemas, graças ao novo profiler introduzido pelo Twig em sua versão 1.18.0. Em primeiro lugar, você verá um novo painel de debug Twig na barra de ferramentas de debug que lhe fornece uma visão geral das principais estatísticas do Twig:
Tumblr media
Se você clicar nesse painel, verá todas as informações coletadas pelo profiler Twig. Primeiro, você obterá as principais estatísticas Twig da página, que é a primeira coisa a verificar quando algo der errado:
Tumblr media
Em segundo lugar, você verá a lista completa dos templates usados ​​para renderizar a página, incluindo os templates de bundles de terceiros e os fragmentos de template:
Tumblr media
Por fim, você terá o gráfico de chamadas completo executado pelo Twig para renderizar a página. Essa informação é útil principalmente quando as coisas não funcionam ou quando os seus templates renderizam lentamente. Graças às informações detalhadas, você descobrirá os gargalos de desempenho instantaneamente:
Tumblr media
O Symfony 2.7 habilita o profiler Twig automaticamente quando a aplicação usa o modo de depuração. Se você estiver usando o Twig fora do Symfony, você deve registrar a nova extensão profiler antes de renderizar os templates:
$profile = new Twig_Profiler_Profile(); $twig->addExtension(new Twig_Extension_Profiler($profile)); // render templates ... $dumper = new Twig_Profiler_Dumper_Text(); echo $dumper->dump($profile);
Além do dumper de texto básico (que é o utilizado pelo Symfony), o Twig também inclui um dumper avançado para a saída das informações do gráfico de chamadas em um formato compatível com o Profiler Blackfire.
Tradução de: “New in Symfony 2.7: Twig Profiler porJavier Eguiluz” 
0 notes
novidades-symfony · 10 years ago
Text
Novidade no Symfony 2.7: Melhorias no Componente Serializer
No Symfony 2.7, o componente Serializer foi muito melhorado com várias funcionalidades novas. Este artigo apresenta as mais importantes.
Suporte para grupos de serialização nos arquivos XML e YAML.
Há alguns meses atrás, já havíamos falado sobre a definição de grupos de serialização através de anotações. Antes de usá-las, você só tem que habilitá-las com a opção de configuração enable_annotations:
# app/config/services.yml framework:    # ...    serializer: { enable_annotations: true }
Para os que não gostam de anotações, agora é possível definir grupos de serialização através de arquivos XML ou YAML. O componente Serializer irá procurar por arquivos serialization.xml ou serialization.yml localizados no diretório Resources/config/ de seus bundles. Além disso, ele vai procurar por qualquer arquivo XML ou YAML localizado dentro do diretório Resources/config/serialization/.
Independentemente do formato de configuração usado, os metadados gerados por esses grupos de serialização podem ser armazenados em cache utilizando o Cache do Doctrine. Isso irá fornecer uma melhoria de desempenho da sua aplicação. Por exemplo, se o servidor fornece suporte para APC ou APCu, habilite o cache do serializer com a seguinte configuração:
# app/config/config.yml framework:    serializer:        cache: serializer.mapping.cache.apc
Novo ObjectNormalizer
No Symfony 2.6 haviam dois tipos de normalizadores: GetSetMethodNormalizer e PropertyNormalizer. O Symfony 2.7 introduz um novo normalizador chamado ObjectNormalizer. A principal vantagem sobre o GetSetMethodNormalizer básico é que ele suporta propriedades protegidas e privadas e até mesmo propriedades que só definem métodos getter/isser:
class MyObject {    public $foo = 'foo';    public getBar()    {       return 'bar';    }    public isBaz()    {        return true;    } } use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; $normalizer = new ObjectNormalizer(); $result = $normalizer->normalize(new MyObject()); // $result = ['foo' => 'foo', 'bar' => 'bar', 'baz' => true]
Para simplificar a criação das coisas, esse novo normalizador, bem como os grupos de serialização, são ativados por padrão no FrameworkBundle.
Capacidade de mudar os nomes das propriedades
O GetSetMethodNormalizer fornece um mecanismo básico para ajustar a conversão do nome da propriedade para o nome do método getter/setter:
// ... $normalizer = new GetSetMethodNormalizer(); $normalizer->setCamelizedAttributes(array('first_name')); $serializer = new Serializer(array($normalizer), array($encoder));
Esse código permite que a propriedade first_name seja corretamente convertida no método getFirstName() em vez do método errado getFirst_name().
No 2.7, o método setCamelizedAttributes() torna-se obsoleto em favor de um novo conversor genérico NameConverter. O primeiro conversor de nome disponível torna camel case em snake case e vice-versa:
use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter; use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer; // ... $nameConverter = new CamelCaseToSnakeCaseNameConverter(array('first_name')); $normalizer = new GetSetMethodNormalizer(null, $nameConverter); $serializer = new Serializer(array($normalizer), array($encoder));
Desserializar em um objeto existente
É comum criar novos objetos sempre que você desserializa as informações. No entanto, no Symfony 2.7 você pode desserializar em um objeto existente. Isso pode ser útil, por exemplo, para atualizar um objeto recuperado através ORM Doctrine:
$existingProduct = $this->getDoctrine()->... $product = $this->normalizer->denormalize(    array('bar' => 'bar'),    'AppBundle\\Entity\\Product',    null,    array('object_to_populate' => $existingProduct) ); // $product and $existingProduct are references to the same object
DunglasJsonLdApiBundle
Para melhor exibir os novos recursos do componente Serializer, Kévin desenvolveu um novo bundle chamado DunglasJsonLdApiBundle. Esse bundle permite criar APIs hipermídia com suporte a JSON-LD e Hydra no topo do componente Serializer.
Tradução de: “New in Symfony 2.7: Serializer Component Improvements por Javier Eguiluz” 
0 notes
novidades-symfony · 10 years ago
Text
Transição do Symfony 2.7 para o 3.0... e o Symfony 2.8 está a caminho
O Symfony 2.7 beta 1 será lançado ainda hoje e será o próximo LTS (lançamento Long-Term-Support). Quem ainda estiver usando o Symfony 2.3 tem um ano (até maio de 2016) para atualizar para o Symfony 2.7.
O Symfony 2.7 é uma versão especial. Mesmo vindo com mais de 100 novos recursos (grandes e pequenos), muito trabalho foi realizado no framework em avisos de descontinuação. Esses avisos ajudam a atualizar facilmente para o Symfony 3.0 a partir de hoje, e eles também garantem que tudo funcionará como esperado no núcleo do framework: compatibilidade entre o Symfony 2.7 e o 3.0, quando aplicável, sem uso de recursos obsoletos no núcleo do framework, ...
Como já explicado há alguns meses, a versão 3.0 é uma versão colisão semântica. Na versão 3.0 é permitido quebrar a compatibilidade com versões anteriores; mas não pela adição de novos recursos ou por quebrar a compatibilidade; o Symfony 3.0 quebra compatibilidade porque a camada de compatibilidade introduzida em todas as versões 2.x será removida. Assim, todos os novos recursos da 3.0 já foram introduzidos nas versões 2.x. E porque 2.7 é agora uma versão com recursos congelados, isso significa que todos os recursos para 3.0 já são conhecidos e não podemos tornar mais nada obsoleto... e isso é um problema. Porque todo o trabalho feito na versão 3.0 não terá mais um caminho de atualização fácil a partir de hoje. É claro que existe uma solução.
Conversei com várias pessoas sobre a melhor solução para resolver esse problema e surgiu uma solução simples e eficiente: a necessidade de uma versão 2.8 do Symfony. Essa versão irá funcionar como quaisquer outras versões 2.x, poderemos adicionar novas funcionalidades, tornar obsoletas outras, e fornecer um bom caminho de upgrade para o Symfony 3.0.
O Symfony 2.8 será lançado em novembro de 2015 ao mesmo tempo que o Symfony 3.0. O Symfony 2.8 será uma versão LTS, bem como permitirá que as pessoas ainda tenham um ano para atualizar da versão 2.8 para a 3.2 quando ela for lançada (sendo 3.2 a próxima versão LTS e a primeira da branch 3.x).
Recapitulando:
O Symfony 2.7 será lançado como planejado em maio de 2015 e será uma versão LTS;
O Symfony 2.8 será lançado como planejado em novembro de 2015 e será uma versão LTS também;
O Symfony 3.0 será lançado como planejado em novembro de 2015 ao mesmo tempo que o Symfony 2.8.
Tradução de: “Transition from Symfony 2.7 to 3.0... Symfony 2.8 on its way por Fabien Potencier”
0 notes
novidades-symfony · 10 years ago
Text
Novidade no Symfony 2.7: Profiler Translation
Internacionalizar uma aplicação e garantir que todas as suas mensagens sejam efetivamente traduzidas é uma das tarefas mais frustrantes, incomodas e demoradas que os desenvolvedores devem enfrentar.
O Symfony está decidido a simplificar drasticamente esse processo e ao mesmo tempo aumentar a sua produtividade. É por isso que no Symfony 2.7, a barra de ferramentas de debug irá mostrar um novo painel de tradução.
Esse painel fornece uma visão geral do status da tradução de sua aplicação:
Tumblr media
As mensagens de tradução são divididas em três categorias: defined (as mensagens que já foram traduzidas), missing (as mensagens que faltam traduzir) e same as fallback (mensagens que não são traduzidas, mas estão disponíveis na localidade padrão configurada na aplicação).
Se você clicar no painel da barra de ferramentas, você verá o novo Profiler Translation:
Tumblr media
Essa tabela mostra os detalhes das mensagens da aplicação, incluindo seu domínio, quantas vezes cada uma delas foi utilizada e uma pré-visualização da mensagem traduzida.
Tradução de: “New in Symfony 2.7: Translation Profiler” por Javier Eguiluz”
0 notes
novidades-symfony · 10 years ago
Text
Novidade no Symfony 2.7: Configuração dos Formatos Padrões de Data e Número
Quando você usa o filtro de data para exibir datas nos templates Twig, o Symfony aplica a formatação de data padrão definida na extensão Twig Core. O mesmo vale para valores numéricos formatados com o filtro number_format.
Esses formatos padrão podem ser sobrescritos usando os argumentos de cada filtro:
{{ post.published_at|date("F jS \\a\\t g:ia") }}
No entanto, se você usar a mesma formatação de data ou número personalizada para todos os valores renderizados nos templates da aplicação, essa técnica é muito complicada. No Symfony 2.6 você pode definir a formatação padrão dentro de um controlador da seguinte forma:
$this->get('twig')->getExtension('core')->setDateFormat('d/m/Y', '%d days');
Configurar a formatação padrão para toda a aplicação é ainda mais complexo, porque você geralmente precisa configurar um listener dedicado. O Symfony 2.7 adiciona duas novas opções de configuração Twig para definir a formatação padrão de números e datas, sem a necessidade de escrever código personalizado.
Use a opção de configuração de data sob a chave twig para sobrescrever qualquer uma das opções default de formatação de data:
# app/config/config.ymltwig:    date:        format: d.m.Y, H:i:s        interval_format: '%%d days'
Você também pode definir a opção de fuso horário para usá-la ao formatar datas:
# app/config/config.yml twig:    date:        # ...        timezone: Europe/Paris
Da mesma forma, a formatação padrão de valores numéricos pode agora ser definida usando a nova opção number_format sob a chave twig:
# app/config/config.yml twig:    number_format:        decimals: 2        decimal_point: ','        thousands_separator: '.'
Tradução de: “New in Symfony 2.7: Default Date and Number Format Configuration” por Javier Eguiluz” 
0 notes
novidades-symfony · 10 years ago
Text
Novidade no Symfony 2.7: Layouts de tabela avançados para comandos de console
O componente Console do Symfony é o segundo componente mais popular do Symfony, com mais de oito milhões de downloads no Packagist.org até agora. Um dos principais fatores do seu sucesso é a quantidade de recursos que ele oferece para criar comandos avançados de console. No Symfony 2.7 fomos um passo além e adicionamos suporte para a definição de layouts de tabela avançados.
Adicionado suporte para colspan e rowspan
Da mesma forma que as propriedades conhecidas das tabelas HTML, as novas propriedades colspan e rowspan permitem criar layouts avançados. Essas propriedades são apoiadas pela nova classe TableCell, que deve ser usada para definir layouts de tabela não regulares.
// A table using colspanuse Symfony\Component\Console\Helper\Table; $table = new Table($output); $table    ->setHeaders(array('Column #1 title', 'Column #2 title', 'Column #3 title'))    ->setRows(array(        array('Value #1', 'Value #2', 'Value #3'),        new TableSeparator(),        array(new TableCell('This value spans 3 columns.', array('colspan' => 3))),    )) ; $table->render();
+-----------------+-----------------+------------------+ | Column #1 title | Column #2 title | Column #3 title  | +-----------------+-----------------+------------------+ | Value #1        | Value #2        | Value #3         | +-----------------+-----------------+------------------+ | This value spans 3 columns.                          | +-----------------+-----------------+------------------+
// A table using rowspan use Symfony\Component\Console\Helper\Table; $table = new Table($output); $table    ->setHeaders(array('Column #1 title', 'Column #2 title', 'Column #3 title'))    ->setRows(array(        array(            new TableCell('Value #1 spanning two rows.', array('rowspan' => 2)),            'Value #2 - 1',            'Value #3 - 1',        ),        array('Value #2 - 2', 'Value #3 - 2'),    )) ; $table->render();
+-------------------+-----------------+------------------+ | Column #1 title   | Column #2 title | Column #3 title  | +-------------------+-----------------+------------------+ | Value #1 spanning | Value #2 - 1    | Value #3 - 1     | | two rows.         | Value #2 - 2    | Value #3 - 2     | +-------------------+-----------------+------------------+
Essas duas propriedades também podem ser combinadas na mesma tabela, permitindo criar qualquer layout que você possa imaginar. Confira os testes do helper Table para ver mais exemplos de uso.
Adicionado suporte para vários cabeçalhos
Um bom efeito colateral da adição do suporte para as propriedades colspan e rowspan é que as tabelas agora suportam cabeçalhos de várias linhas, que são ideais para exibir um título de tabela geral e alguns títulos de colunas específicas:
use Symfony\Component\Console\Helper\Table; $table = new Table($output); $table    ->setHeaders(array(        array(new TableCell('Main table title', array('colspan' => 3))),        array('Column #1 title', 'Column #2 title', 'Column #3 title'),    ))    ->setRows(array(        // ...    )) ; $table->render();
+-----------------+-----------------+-----------------+ | Main table title                                    | +-----------------+-----------------+-----------------+ | Column #1 title | Column #2 title | Column #3 title | +-----------------+-----------------+-----------------+ | ...                                                 | +-----------------+-----------------+-----------------+
Tradução de: “New in Symfony 2.7: Advanced table layouts for console commands por Javier Eguiluz”
0 notes