Category Archives: Symfony2

Symfony2 Tutorial Part 2: A Simple Blog

About this tutorial

Now we can start to create a simple blog application with our newly set up symfony2 project.

If you still have your AcmeDemoBundle you can remove it now. (http://blog.quadspot.de/wordpress/symfony2/remove-acmedemobundle-in-a-symfony2-project)

  1. First we create a New Bundle which will contain our blog. The easiest way to do this, is to use the interactive generators.
    If you simply type:

    php app/console generate:bundle

    the generator will ask for all the needed parameters.
    A quicker way is to pass all the required options directly to the generator:

    php app/console generate:bundle --namespace=Quadspot/BlogBundle --dir=src --structure --format=yml --no-interaction
    

    You can use any format you like. I personaly prefer yml.

    This will generate our BlogBundle in src/Quadspot/BlogBundle.

    Hint: If you want to know more about the options you can use the –help option

    php app/console generate:bundle --help

    The generator also registered new Bundle in app/AppKernel.php and created the required routes in app/config/routing.yml.

    Hint: The new generated Bundle comes with a simple default application which is already fully functional. You can access it under http://symfony.yourhostname/app_dev.php/hello/symfony Look into app/config/routing.yml, src/Quadspot/BlogBundle/Controller/DefaultControler.php, src/Quadspot/BlogBundle/Resources/views/Default/index.html.twig and if you used a format other than annotations src/Quadspot/BlogBundle/Resources/config/routing.xml, src/Quadspot/BlogBundle/Resources/config/routing.php or src/Quadspot/BlogBundle/Resources/config/routing.yml if you want to learn more about it.

  2. Now we use the generator to create our entity:
    php app/console doctrine:generate:entity --entity=QuadspotBlogBundle:Post --fields="title:string(255) created_at:datetime updated_at:datetime body:text" --format=yml --no-interaction
    

    This command will have created two files: src/Quadspot/BlogBundle/Entity/Post.php

    <?php //src/Quadspot/BlogBundle/Entity/Post.php
    namespace Quadspot\BlogBundle\Entity;
    
    use Doctrine\ORM\Mapping as ORM;
    
    /**
     * Post
     */
    class Post
    {
        /**
         * @var integer
         */
        private $id;
    
        /**
         * @var string
         */
        private $title;
    
        /**
         * @var \DateTime
         */
        private $created_at;
    
        /**
         * @var \DateTime
         */
        private $updated_at;
    
        /**
         * @var string
         */
        private $body;
    
    
        /**
         * Get id
         *
         * @return integer 
         */
        public function getId()
        {
            return $this->id;
        }
    
        /**
         * Set title
         *
         * @param string $title
         * @return Post
         */
        public function setTitle($title)
        {
            $this->title = $title;
    
            return $this;
        }
    
        /**
         * Get title
         *
         * @return string 
         */
        public function getTitle()
        {
            return $this->title;
        }
    
        /**
         * Set created_at
         *
         * @param \DateTime $createdAt
         * @return Post
         */
        public function setCreatedAt($createdAt)
        {
            $this->created_at = $createdAt;
    
            return $this;
        }
    
        /**
         * Get created_at
         *
         * @return \DateTime 
         */
        public function getCreatedAt()
        {
            return $this->created_at;
        }
    
        /**
         * Set updated_at
         *
         * @param \DateTime $updatedAt
         * @return Post
         */
        public function setUpdatedAt($updatedAt)
        {
            $this->updated_at = $updatedAt;
    
            return $this;
        }
    
        /**
         * Get updated_at
         *
         * @return \DateTime 
         */
        public function getUpdatedAt()
        {
            return $this->updated_at;
        }
    
        /**
         * Set body
         *
         * @param string $body
         * @return Post
         */
        public function setBody($body)
        {
            $this->body = $body;
    
            return $this;
        }
    
        /**
         * Get body
         *
         * @return string 
         */
        public function getBody()
        {
            return $this->body;
        }
    }
    

    and src/Quadspot/BlogBundle/Resources/config/doctrine/Post.orm.yml

    #src/Quadspot/BlogBundle/Resources/config/doctrine/Post.orm.yml
    Quadspot\BlogBundle\Entity\Post:
        type: entity
        table: null
        id:
            id:
                type: integer
                id: true
                generator:
                    strategy: AUTO
        fields:
            title:
                type: string
                length: '255'
            created_at:
                type: datetime
                length: null
            updated_at:
                type: datetime
                length: null
            body:
                type: text
                length: null
        lifecycleCallbacks: {  }
    

    Before we create our database schema we will change the name of the Post table in the database from Post to quadspot_blogbundle_post.
    Simply edit the src/Quadspot/BlogBundle/Resources/config/doctrine/Post.orm.yml file so that it looks like this:

    #src/Quadspot/BlogBundle/Resources/config/doctrine/Post.orm.yml
    Quadspot\BlogBundle\Entity\Post:
        type: entity
        table: quadspot_blogbundle_post
        id:
            id:
                type: integer
                id: true
                generator:
                    strategy: AUTO
        fields:
            title:
                type: string
                length: '255'
            created_at:
                type: datetime
                length: null
            updated_at:
                type: datetime
                length: null
            body:
                type: text
                length: null
        lifecycleCallbacks: {  }
    

    Now we can create our database schema:

    php app/console doctrine:schema:create

    Hint: If your haven’t yet created your database you can do this with:

    php app/console doctrine:database:create
  3. Next we wil create a new controler and templates do retrieve the dada from the database. We use the generator functionality of symfony2 again.
    php app/console generate:doctrine:crud --entity=QuadspotBlogBundle:Post --format=yml --no-interaction

    We just have to manually add the new routing configuration to our routing file src/Quadspot/BlogBundle/Resources/config/routing.yml.

    #src/Quadspot/BlogBundle/Resources/config/routing.yml
    QuadspotBlogBundle_post:
        resource: "@QuadspotBlogBundle/Resources/config/routing/post.yml"
        prefix:   /
    

    Hint: We changed the prefix to /.

  4. We can now remove the DefaultController and its Templates.
    Delete src/Quadspot/BlogBundle/Controller/DefaultController.php and src/Quadspot/BlogBundle/Resources/views/Default/index.html.twig
  5. Now we just need some entries in the database to be displayed in our blog. Open a mysql console:
    mysql -usymfony -ppa$$word symfony
    INSERT INTO `symfony`.`quadspot_blogbundle_post` (`id` ,`title` ,`created_at` ,`updated_at` ,`body`) VALUES ('1' , 'Lorem Ipsum', '2014-09-22 10:30:00', '2014-09-22 10:30:00', 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam');
    INSERT INTO `symfony`.`quadspot_blogbundle_post` (`id` ,`title` ,`created_at` ,`updated_at` ,`body`) VALUES ('2' , 'Lorem Ipsum 2', '2014-09-22 10:35:00', '2014-09-22 10:35:00', 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut');

And that’s it for now. In the next part we will create our backend so that we can add new posts and edit and delete existing ones.

Symfony2 Tutorial Part 1: Project Setup

About this tutorial:

In this tutorial I will show you how to setup a new symfony2 project on a local webserver. I use Linux Mint 17 with the following packages installed:

apache2 apache2-mpm-prefork curl mysql-server php-apc php5 php5-cli php5-intl php5-sqlite php5-xdebug php5-xsl phpmyadmin

Lets get started.

Install symfony2

  1. Install composer:
    http://getcomposer.org/download/
    I use the following command as root:

    curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/bin
  2. Create a MySQL database:
    On your MySQL console:

    CREATE DATABASE symfony;
    GRANT ALL ON symfony.* TO symfony@'localhost' IDENTIFIED BY 'pa$$word';
    
  3. Get symfony2:
    Open a terminal and type

    composer.phar create-project symfony/framework-standard-edition /your/symfony/dir '2.5.*'

    HINT: You can replace '2.5.*' with your preferred symfony2 version. For example use '2.3.*' if you want to use the LTS version.

  4. Configure ACL:
    How to install and configure acl: http://wiki.ubuntuusers.de/ACL
    Open a terminal and type in the following commands.

    cd /your/symfony/dir
    HTTPDUSER=`ps aux | grep -E '[a]pache|[h]ttpd|[_]www|[w]ww-data|[n]ginx' | grep -v root | head -1 | cut -d\  -f1`
    sudo setfacl -R -m u:"$HTTPDUSER":rwX -m u:`whoami`:rwX app/cache app/logs
    sudo setfacl -dR -m u:"$HTTPDUSER":rwX -m u:`whoami`:rwX app/cache app/logs
    

Setup your webserver

  1. Create /etc/apache2/sites-available/symfony.conf as root with the following content.
    <VirtualHost *:80>
    	ServerName symfony
            ServerAlias symfony.yourhostname
    
    	ServerAdmin webmaster@localhost
    	DocumentRoot /your/symfony/dir/web
    
    	<Directory />
    		DirectoryIndex app.php
    		Options FollowSymLinks
    		AllowOverride All
    		Require all granted
    	</Directory>
    	<Directory "/your/symfony/dir/web">
    		Options Indexes FollowSymLinks MultiViews
    		AllowOverride All
    		Require all granted
    	</Directory>
    
    	ErrorLog ${APACHE_LOG_DIR}/symfony_error.log
    	CustomLog ${APACHE_LOG_DIR}/symfony_access.log combined
    
    </VirtualHost>
    
    
  2. run
    sudo a2ensite symfony

    and

    sudo service apache2 reload
  3. edit /etc/hosts and add:
    127.0.0.1 symfony.yourhostname symfony

HINT: The apache mod rewrite is not enabled by default. You enable it with:

sudo a2enmod rewrite
sudo service apache2 restart

HINT: you can check if you have configured your server correctly with:

cd /your/symfony/dir
php app/check.php

In most cases the date.timezone parameter in both /etc/php5/apache2/php.ini and /etc/php5/cli/php.ini is not set. In my case it’s: date.timezone = "Europe/Berlin".
You can find a list of different timezones here: http://php.net/manual/en/timezones.php

Access your symfony2 application

Thats it. You can now access your symfony2 application (development) via
http://symfony.yourhostname/app_dev.php
And the production environment via
http://symfony.yourhostname

At this point you will get an 404 error because you don’t have any content to be shown yet. Don’t worry we will add something in the next part.

Setting up a Symfony2 Project with FOSUserBundle, SonataUserBundle and SonataAdminBundle

Tested with Symfony Standard 2.0.7 (http://symfony.com/download?v=Symfony_Standard_Vendors_2.0.7.tgz)

This is basically a merge of the installation-guides of FOSUserBundle (https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/Resources/doc/index.md), SonataAdminBundle (http://sonata-project.org/bundles/admin/master/doc/reference/installation.html) and SonataUserBundle (http://sonata-project.org/bundles/user/master/doc/reference/installation.html)

  1. Add
    [FOSUserBundle]
        git=git://github.com/FriendsOfSymfony/FOSUserBundle.git
        target=bundles/FOS/UserBundle
    
    [SonatajQueryBundle]
        git=http://github.com/sonata-project/SonatajQueryBundle.git
        target=/bundles/Sonata/jQueryBundle
    
    [SonataAdminBundle]
        git=http://github.com/sonata-project/SonataAdminBundle.git
        target=/bundles/Sonata/AdminBundle
    
    [MenuBundle]
        git=https://github.com/KnpLabs/KnpMenuBundle.git
        target=/bundles/Knp/Bundle/MenuBundle
    
    [KnpMenu]
        git=https://github.com/KnpLabs/KnpMenu.git
        target=/knp/menu
    
    [SonataUserBundle]
        git=git://github.com/sonata-project/SonataUserBundle.git
        target=/bundles/Sonata/UserBundle
    
    [SonataEasyExtendsBundle]
        git=git://github.com/sonata-project/SonataEasyExtendsBundle.git
        target=/bundles/Sonata/EasyExtendsBundle
    
    [SonataDoctrineORMAdminBundle]
        git=http://github.com/sonata-project/SonataDoctrineORMAdminBundle.git
        target=/bundles/Sonata/DoctrineORMAdminBundle

    to your deps file. (Don’t forget the trailing newline.)

  2. Run
    php bin/vendors install --reinstall
  3. Add the namespaces to app/autoload.php
    // app/autoload.php
    $loader->registerNamespaces(array(
        // ...
        'FOS'              => __DIR__.'/../vendor/bundles',
        'Sonata'           => __DIR__.'/../vendor/bundles',
        'Application'      => __DIR__,
        'Knp'              => array(
                              __DIR__.'/../vendor/bundles',
                              __DIR__.'/../vendor/knp/menu/src',
                              ),
        // ...
    ));
  4. Enable the bundles in app/AppKernel.php
    // app/AppKernel.php
        public function registerBundles()
        {
            $bundles = array(
                // ...
                new FOS\UserBundle\FOSUserBundle(),
                new Sonata\jQueryBundle\SonatajQueryBundle(),
                new Sonata\AdminBundle\SonataAdminBundle(),
                new Sonata\DoctrineORMAdminBundle\SonataDoctrineORMAdminBundle(),
                new Knp\Bundle\MenuBundle\KnpMenuBundle(),
                new Sonata\UserBundle\SonataUserBundle('FOSUserBundle'),
                new Sonata\EasyExtendsBundle\SonataEasyExtendsBundle(),
                // ...
            );
            // ...
        }
  5. Add
    # app/config/config.yml
    fos_user:
        db_driver: orm
        firewall_name: main
        user_class: Application\Sonata\UserBundle\Entity\User

    to app/config/config.yml

  6. Run
    php app/console sonata:easy-extends:generate SonataUserBundle
  7. Add the new Bundle to app/AppKernel.php
    // app/AppKernel.php
        public function registerbundles()
        {
            $bundles = array(
                // Application Bundles
                // ...
                new Application\Sonata\UserBundle\ApplicationSonataUserBundle(),
                // ...
            );
            // ...
        }
  8. Add
    # app/config/routing.yml
    fos_user_security:
        resource: "@FOSUserBundle/Resources/config/routing/security.xml"
    
    fos_user_profile:
        resource: "@FOSUserBundle/Resources/config/routing/profile.xml"
        prefix: /profile
    
    fos_user_register:
        resource: "@FOSUserBundle/Resources/config/routing/registration.xml"
        prefix: /register
    
    fos_user_resetting:
        resource: "@FOSUserBundle/Resources/config/routing/resetting.xml"
        prefix: /resetting
    
    fos_user_change_password:
        resource: "@FOSUserBundle/Resources/config/routing/change_password.xml"
        prefix: /change-password
    
    admin:
        resource: '@SonataAdminBundle/Resources/config/routing/sonata_admin.xml'
        prefix: /admin
    
    _sonata_admin:
        resource: .
        type: sonata_admin
        prefix: /admin
    
    soanata_user:
        resource: '@SonataUserBundle/Resources/config/routing/admin_security.xml'
        prefix: /admin

    to app/config/routing.yml

  9. Add the following to app/config/security.yml
    # app/config/security.yml
    security:
        encoders:
            FOS\UserBundle\Model\UserInterface: sha512
    
        role_hierarchy:
            ROLE_ADMIN:       ROLE_USER
            ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_SONATA_ADMIN, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
            SONATA:
                - ROLE_SONATA_PAGE_ADMIN_PAGE_EDIT  # if you are not using acl then this line must be uncommented
    
        providers:
            fos_userbundle:
                id: fos_user.user_manager
    
        firewalls:
    
            # -> custom firewall for the admin area of the URL
            admin:
                pattern:      /admin(.*)
                form_login:
                    provider:       fos_userbundle
                    login_path:     /admin/login
                    use_forward:    false
                    check_path:     /admin/login_check
                    failure_path:   null
                logout:
                    path:           /admin/logout
                anonymous:    true
            # -> end custom configuration
    
            # defaut login area for standard users
            main:
                pattern:      .*
                form_login:
                    provider:       fos_userbundle
                    login_path:     /login
                    use_forward:    false
                    check_path:     /login_check
                    failure_path:   null
                logout:       true
                anonymous:    true
    
    # ...
    
        access_control:
            # URL of FOSUserBundle which need to be available to anonymous users
            - { path: ^/_wdt, role: IS_AUTHENTICATED_ANONYMOUSLY }
            - { path: ^/_profiler, role: IS_AUTHENTICATED_ANONYMOUSLY }
            - { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
    
            # -> custom access control for the admin area of the URL
            - { path: ^/admin/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
            - { path: ^/admin/logout$, role: IS_AUTHENTICATED_ANONYMOUSLY }
            - { path: ^/admin/login-check$, role: IS_AUTHENTICATED_ANONYMOUSLY }
            # -> end
    
            - { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
            - { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
    
            # Secured part of the site
            # This config requires being logged for the whole site and having the admin role for the admin part.
            # Change these rules to adapt them to your needs
            - { path: ^/admin, role: [ROLE_ADMIN, ROLE_SONATA_ADMIN] }
            - { path: ^/.*, role: IS_AUTHENTICATED_ANONYMOUSLY }
    # ...
  10. Update your DB-Schema. Run
    php app/console doctrine:schema:update --force
  11. Now, install the assets from the different bundles:
    php app/console assets:install web
  12. Clear your cache:
    php app/console cache:clear
  13. You can create an admin-user and a test-user with the following commands.
    php app/console fos:user:create admin admin@example.com password --super-admin
    php app/console fos:user:create testuser test@example.com password
  14. You need to enable your translator in /app/config.yml.
  15. Create app/Application/Sonata/UserBundle/Recources/translations/SonataUserBundle.en.ymlwith the following content:
    # app/Application/Sonata/UserBundle/Recources/translations/SonataUserBundle.en.yml
    form:
      label_username:             Username
      label_email:                Email
      label_plain_password:       Password (Plain)
      label_groups:               Groups
      label_roles:                Roles
      label_locked:               Locked
      label_expired:              Expired
      label_enabled:              Enabled
      label_credentials_expired:  Credentials Expired
      label_name:                 Name
    
    list:
      label_username:             Username
      label_email:                Email
      label_enabled:              Enabled
      label_locked:               Locked
      label_created_at:           Created At
      label_roles:                Roles
      label_name:                 Name
    
    filter:
      label_username:             Username
      label_locked:               Locked
      label_email:                Email
      label_id:                   ID
      label_name:                 Name

Remove the AcmeDemoBundle in a Symfony2 project

Tested with Symfony Standard 2.0.6 (http://symfony.com/download?v=Symfony_Standard_Vendors_2.0.6.tgz)

  1. Delete src/Acme folder.
  2. Delete
    // app/AppKernel.php
    $bundles[] = new Acme\DemoBundle\AcmeDemoBundle();

    from app/AppKernel.php

  3. Delete
    # app/config/routing_dev.yml
    # AcmeDemoBundle routes (to be removed)
    _acme_demo:
        resource: "@AcmeDemoBundle/Resources/config/routing.yml"
    

    from app/config/routing_dev.php.

  4. Clear your cache:
    php app/console cache:clear