To restore a single database from a database dump which contains multiple databases:

mysql -u username -p --one-database data_base_to_restore < backup.sql

or if you have a bz2 file

mysql -u username -p --one-database data_base_to_restore < bzcat backup.sql.bz2
| Symfony 2 Utils | Laisser un commentaire

If you need to test your code against the same version of PHP that you use in production, or if you want to test out the cutting edge features of PHP then you will probably need to compile php from source.

This is easier than it sounds, and I know it is easy with debian or ubuntu (and hence aptitude)

First, download the source from php.net then unpack it somewhere in your home directory:

cd /home/daniel/source
tar -xvzf php-5.3.10.tar.gz

Then you can try and configure it as follows

./configure

It will probably fail with a message stating that some library was not found. It is necessary to install all the dependent libraries before you can compile from source, fortunately this is very easy in debian/ubuntu

apt-get build-dep php5

Hopefully you now have all the dependencies required and you can configure again:

./configure

OK? ok. However this will configure a very minimal build. In general you will want to compile many modules (e.g. mysql, gd, xml, curl, etc). This is a typical configuration that I use:

'./configure'  '--with-apxs2=/usr/bin/apxs2' '--with-curl' '--with-gd' '--with-mcrypt' '--with-mhash' '--with-mysql' '--with-pdo-mysql' '--with-snmp' '--enable-soap' '--with-openssl' '--with-xsl' '--with-config-file-path=/etc/php5' '--enable-bcmath' '--with-zlib' '--enable-sysvsem' '--with-gd' '--with-jpeg-dir=/usr/lib' '--with-png-dir=/usr/lib' '--with-readline' '--enable-mbstring' '--enable-intl' '--prefix=/opt/php-5.3.10'

Note the prefix option. This is important as it is the directory where the code will be installed. By default it will install to usr/lib but this might become confusing later on. So I put all my cusom code in /opt.

OK. Now we can compile the code

make

Hit return and you will see many lines of as yet incomprehensible messages. This will continue for a while depending on the speed of your computer, it can take 10-15 minutes on my laptop.

When it has finished you should not see any errors, and the final lines should look as follows:

Generating phar.php
Generating phar.phar
PEAR package PHP_Archive not installed: generated phar will require PHP's phar extension be enabled.
invertedregexiterator.inc
pharcommand.inc
directorygraphiterator.inc
directorytreeiterator.inc
clicommand.inc
phar.inc

Build complete.
Don't forget to run 'make test'.

Now you can install with

make install

This will configure apache automatically (at least it seems to that for me) but if you want to use php from the command line you will need to symlink the php binary in your bin path (or add /opt/php-blah to you environment path).

cd /usr/bin
ln -s /opt/php-x.x.x/bin/php php

Et voila:


root@dan-x61:/usr/bin# php --version
PHP 5.3.10 (cli) (built: Feb 15 2012 09:30:11)
Copyright (c) 1997-2012 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2012 Zend Technologies
| Symfony 2 Utils | Laisser un commentaire

This article is going to explain /how to/ correctly create a backoffice
(admin panel + crud) in Symfony 2.

Generally, for any web project, you need a back office to manager
entities, data, users and groups and, fortunately today there are a
number of bundles for Symfony 2 which allow you to create an admin panel
quickly.

(.. because it is boring to redevelop phpmyadmin we can save ourselves
the effort and reutilise existing Symfony2 bundles)

What bundles do you need to install?

[FOSUserBundle]
    git=git://github.com/FriendsOfSymfony/FOSUserBundle.git
    target=bundles/FOS/UserBundle

[SonatajQueryBundle]
    git=https://github.com/sonata-project/SonatajQueryBundle.git
    target=/bundles/Sonata/jQueryBundle

[SonataAdminBundle]
    git=https://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=https://github.com/sonata-project/SonataDoctrineORMAdminBundle.git
    target=/bundles/Sonata/DoctrineORMAdminBundle

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',
                          ),
    // ...
));

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(),
            // ...
        );
        // ...
    }

Add

# app/config/config.yml
fos_user:
    db_driver: orm
    firewall_name: main
    user_class: Application\Sonata\UserBundle\Entity\User

Run

php app/console sonata:easy-extends:generate SonataUserBundle

It is going to generate an Application UserBundle, but you can use your own.
To develop your understanding, do this by default and try after to readapt the code with your UserBundle

Add the new Bundle to app/AppKernel.php

// app/AppKernel.php
    public function registerbundles()
    {
        $bundles = array(
            // Application Bundles
            // ...
            new Application\Sonata\UserBundle\ApplicationSonataUserBundle(),
            // ...
        );
        // ...
    }

Add routing

# 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

sonata_user_impersonating:
    pattern: /
    defaults: { _controller: SonataPageBundle:Page:catchAll }

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 }
# ...

Do theses commands for the end

php app/console doctrine:schema:update --force
php app/console assets:install web
php app/console cache:clear
php app/console fos:user:create admin admin@example.com password --super-admin

You need to enable your translator in /app/config.yml

framework:
    translator: ~

That’s it!!!
As you can see, a lotof things must be done for it to
work

But after this initial step you will only need to modify the XML files
and the Admin class ((plural?)) of your new bundles.

With this demo you should have a functioning admin interface for « user »
and « group » entities. This is accomplished only with the following 2
files:
- Setup UserAdmin Class (found in AdminFolder in the bundle SonataUserBundle) (see more info in SonataAdminBundle Doc)
- Setup admin_orm.xml (found in Ressources/coinfig in the bundle SonataUserBundle) (see more info in SonataAdminBundle Doc)

When you need to administer more entities in your application, editing
these two files is all you will need to do.

For more information, see: http://sonata-project.org/bundles/admin/master/doc/index.html

| Symfony 2 Utils | Laisser un commentaire

I have been using my server recently to maintain my TODO lists, but I have just moved into a new apartment and I do not yet have the internet, so… I could copy the files to my laptop, go home, then copy them back to the server when I am at work, but this is tedious and I risk losing my data everytime, so I thought it would be nice to syncronize my laptop with the folders on the server.

Dropbox does this, but I don’t like dropbox, I guess I don’t like the idea of putting my data in somebody else hands. So about 2 months ago I had a go at writing my own sync daemon. Using Python with GIT and pyinotify. I soon lost interest however so it doesnt work.

More recently I re-googled and found SparkleShare and DVCS-Autosync.

SparkleShare

SparkleShare is quite user friendly, using a GTK interface, I use « quite » because it still required some technical knowledge for me to get it working. Unfortunately it hijacked my .ssh configuration and left me scratching my head when pushing changes to my GiTHub repository. (It was necessary to remove the .ssh/config file which sparkle share added).

DVCS-Autosync

Install with:

apt-get install dvcs-autosync

The package is not available in squeeze so you will need to upgrade to wheezy.

DVCS (Distributed Version Control System) autosync is very simple and it works.

The only catch is that it requires a Jabber account, I chose to install a jabber server on my web server — but you can alternatively just create an account at jabber.org.

Installing a jabber server was easier than I imagined:

apt-get install prosody

Then after following a few simple steps it was up and running.

Now my files are synconized between my two laptops, my desktop machine and my servers shell account.

(original article here)

| Symfony 2 Utils | Laisser un commentaire

Dans le cadre du développement d’un bundle e-commerce pour l’une de nos applications, j’ai été confronté au besoin d’utiliser des constantes.

Pour rappel, une constante se différencie d’une variable par le fait qu’elle ne peut être modifiée pendant l’exécution du script.

J’ai d’abord pensé à utiliser la manière simple et native de PHP… Mais dans quel fichier placer ma définition ? Rien n’a été prévu, ça n’est évidemment pas la bonne méthode.

Je propose d’aborder deux méthodes différentes. La première, générique, permettra la définition de paramètre globaux pour l’application. La deuxième plus complexe à mettre en place, laissera la possibilité d’une hiérarchisation plus élaborée.

Méthode 1 : définir des paramètres globaux

Un projet Symfony 2 contient déjà un fichier permettant de définir des paramètres, la configuration de base du framework en fait d’ailleurs un usage significatif :


# app/config/parameters.ini

[parameters]
    database_driver="pdo_mysql"
    database_host="localhost"
    database_port=""
    database_name="mydb"
    database_user="root"
    database_password=""
    mailer_transport="smtp"
    mailer_host="localhost"
    mailer_user=""
    mailer_password=""
    locale="fr"
    secret="xxxxxxxxxxxxxxxxxxxxxxx"

    admin_email="admin.app@gmail.com"

Ajoutez simplement à la fin de ce fichier le nom du paramètre souhaité et sa valeur (voir ci-dessus). Le dernier paramètre sera ainsi accessible depuis un controller ou tout autre classe (un service par exemple) incluant le container :


class DemoController extends Controller
{
     /**
      * @Route("/", name="_demo")
      * @Template()
      */
     public function indexAction()
     {
         $adminEmail = $this->container->getParameter('admin_email');

         return $this->render('homepage', array('adminEmail' => $adminEmail));
     }

Méthode 2 : Construire un arbre hiérarchisé de paramètres

Grâce au format YML qui permet l’ordonnancement de données, il est possible de définir des arbres de paramètres. Ouvrons par exemple le fichier app/config/config.yml et ajoutez les lignes suivantes à la fin en respectant bien l’indentation :

# app/config/config.yml

mga_user:
    from_email:
    address: contact@MyGreatApp.com
    sender_name: My Great App

Ensuite, dans le bundle le plus pertinent, ajoutez le fichier Configuration.php, s’il n’existe pas déjà. Ce fichier va indiquer à Symfony comment il doit interpréter ces paramètres :

src/MGA/UserBundle/DependencyInjection/Configuration.php


namespace MGA\UserBundle\DependencyInjection;

use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
/**
 * This is the class that validates and merges configuration from your app/config files
 *
 * To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html#cookbook-bundles-extension-config-class}
 */
class Configuration implements ConfigurationInterface {

    /**
     * {@inheritDoc}
     */
    public function getConfigTreeBuilder()
    {
        $treeBuilder = new TreeBuilder();
        $rootNode = $treeBuilder->root('mga_user');

        // Here you should define the parameters that are allowed to
        // configure your bundle. See the documentation linked above for
        // more information on that topic.

        $rootNode->children()
            ->arrayNode('from_email')
            ->addDefaultsIfNotSet()
                ->children()
                    ->scalarNode('address')->cannotBeEmpty()->end()
                    ->scalarNode('sender_name')->cannotBeEmpty()->end()
                ->end()
            ->end();

        return $treeBuilder;
    }
}

Et dans le même répertoire le fichier MGAUserExtension.php doit hydrater le container avec ces nouveaux paramètres :


namespace MGA\UserBundle\DependencyInjection;

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;
use Symfony\Component\Config\Definition\Processor;

/**
 * This is the class that loads and manages your bundle configuration
 *
 * To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html}
 */
class MGAUserExtension extends Extension
{
    /**
     * {@inheritDoc}
     */
     public function load(array $configs, ContainerBuilder $container)
     {
         // [...]
         $processor = new Processor();
         $configuration = new Configuration();
         $config = $processor->processConfiguration($configuration, $configs);

         $container->setParameter(
             'from_email',
              array($config['from_email']['address'] => $config['from_email']['sender_name'])
         );

    }
}

L’avantage de cette méthode est que le paramètre peut être autre chose qu’une chaîne de caractères. Le paramètre from_email renverra ici un array(), prêt à être donné au mailer au moment de l’envoi d’un e-mail.

On appelera un paramètre défini avec cette méthode de la même façon qu’avec la précédente :


$this->container->getParameter('from_email');

Les possibilités sont nombreuses quant à la définition d’un arbre de paramètres, le célèbre FOSUserBundle renferme de jolis exemples, visibles ici :
https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/DependencyInjection/Configuration.php

| Symfony 2 Utils | Commentaires fermés

I am working on some Javascript code which is more like the code I usually write in PHP, it is more about buisness logic and so is the sort of thing I usually develop in isolation using unit tests, but this is javascript and runs in browsers. Selenium is unsuitable, and things like QUnit run in browsers. I want to test my script from the CLI.

Google tells me about Rhino. Rhino is a standalone javascript interpreter, so

apt-get install rhino
$ rhino
js> # this is a javascript shell

Cool. I can run javascript, but my script uses JQuery and needs the DOM document, enter Envjs.

Envjs is a script which emulates the DOM environment.

js> Packages.org.mozilla.javascript.Context.getCurrentContext().setOptimizationLevel(-1);

js> load('env.rhino.1.2.js');
js> load('jquery.js');
js> $(document);

Of course you can put all that in a script:

# myscript.js
# without this it doesnt work (I havn't looked into it specifically)
Packages.org.mozilla.javascript.Context.getCurrentContext().setOptimizationLevel(-1);

# load EnvJS
load('env.rhino.1.2.js');

# load JQuery
load('jquery.js');

# load my JQuery plugin
load('myJqueryPlugin.js');

# call my plugin
$.fn.myPlugin();

Now its would be quite simple to run QUnit tests from the command line, or even integrate javascript testing into PHPUnit.

| Symfony 2 Utils | Laisser un commentaire

I wanted to be able to code on one screen and refresh my browser on the another screen, on another computer, just by hitting the <F5> key. The aim was to refresh the screen with one easily accessible key stroke…

Things you will need:

  • SSH
  • The UZBL browser (http://uzbl.org)
  • VIM (or anything which can map a key to a command)

Send a command to UZBL via the command line

  1. Open the UZBL browser.
  2. There should now be two files in /tmp: uzbl_fifo_xxxx and uzbl_socket_****
  3. Send a command to the fifo file as follows: echo "uri http://www.dantleech.com" > /tmp/uzbl_fifo_xxxx

My webpage should now have loaded in your UZBL browser instance. If not, ensure that you have replaced xxxx in the filename with the actual number as listed in your /tmp directory.

Get SSH working

Note:If you want to send commands to UZBL on the SAME MACHINE you can skip this step!

  1. Copy the SSH key to the target machine: ssh-copy-id myuser@mymachine
  2. Send commands to the browser: ssh myuser@mymachine 'echo "reload" > /tmp/uzbl_fifo_xxxx' (note the single quotes)

You will need to relace usbl_fifo_xxxx with the actual filename. This command should reload the current page in the browser.

Map to a hotkey in VIM

Or the editor of your choice …

:map <F5> :!ssh myuser@mymachine 'echo "reload" > /tmp/uzbl_fifo_xxxx'<CR><CR>

| Symfony 2 Utils | Laisser un commentaire

For you guys working at phpStorm, your IDE rocks AND makes chillout coding in Symfony2 …

| Symfony 2 Utils | Laisser un commentaire

Project workflow with GITHUB

Here at Ylly we made an early decision to use GITHub for our CMS. Mainly because the tools we decided
to use also use GITHub, namely Symfony2 and Doctrine2.

GITHub is also attractive as it allows and actively encourages the community, I dont like social networks
in general, but in this context I can make an exception. GiTHub is very good.

The only downside is that our CMS is currently not open source and our data for the CMS will never
be open source, and we have an every growing number of separate private repositories, and the pricing for private repositories
on github is based on the number of repositories, and not the amount of space they take up, so we
have to take cost into account when creating new private repositories which is not great.

A little later we discovered that we could convert our account into an organization and
that an organization account permits multiple administrators and other things which
I cannot remember right now …

But it wasnt until recently that we realised the full potential of the GITHub workflow, up until recently we
had all been pushing to our master account at github.com:ylly/yProx. Without realising
that we can each fork a version of the master repository to our own
accounts and the repositories will remain private.

This prompted us to use the great github pull request feature to review pull changes into our
master repository. So our workflow is now:

  • Fork the CMS from the main Ylly organization account
  • Clone the repository from the developers github account
  • The developer can then modify the code, e.g. fix a ticket
  • Commit and push the changes back to the developers repository
  • The developer can then create a pull request
  • The developer can then move on to the next ticket

Then later on when we want to update the master repository and deploy the code we can
review each change and either accept or reject it.

| Symfony 2 Utils | Laisser un commentaire

Hello,

For people who dont know, theres an amazing plugin Eclipse called Cubictest à http://cubictest.seleniumhq.org/

CubicTest is a graphical Eclipse plug-in for writing Selenium and Watir tests. It makes web tests faster and easier to write, and provides abstractions to make tests more robust and reusable.
CubicTest features an innovative test recorder and test runner based on Selenium RC which are fully integrated with the graphical test editor. Tests can also run standalone from Maven 2.

It rocks but you need to have Eclipse :P
For vim users be patient ;-)

Jordan

| Symfony 2 Utils | Laisser un commentaire