Robert Basic's blog

Posts tagged 'php'

Recording screencasts of OSS contributions

by Robert Basic on April 19, 2017.

I enjoy contributing to open source projects, and I learn a lot while doing it. When someone asks me for advice on how to improve as a programmer, I usually tell them to find an open source project that interests them, and start contributing.

Easier said than done.

I’ve been contributing since… early 2009 I think, when I joined the Zend Framework mailing list.

To try and bring closer contributing to beginners, I decided to start recording screencasts of me doing open source contributions. To give a glimpse of how I do it.

So far I have created 4 of them and uploaded on YouTube. The quality is not perfect, but I think it’s good enough. There’s no video editing, I want to show how I really do it, no fixing of mistakes, no retakes. I use zoom to start a “meeting” and then share and record the screen. It’s actually the best screencasting software for Fedora I’ve found, and it’s not even a screencasting software ¯\(ツ)/¯

While doing these screencasts I also realised that I quite enjoy doing this and the whole process has the added bonus of me actual doing rubber ducking, because, well, I talk all the time as I do things.

Also, potential clients and employers can get a peak at how I work.

Happy hackin’!

Tags: about, open source, php, screencast.
Categories: Blablabla, Programming.

Need help on your PHP projects? Let's talk!

Read-only Symfony form field

by Robert Basic on April 10, 2017.

The future me will be grateful for this post. I always get it wrong the first time.

To set a Symfony form field as a read-only, we can’t set the readonly attribute as an option on that field:

src/AppBundle/Form/FooType.php

<?php
declare(strict_types=1);

namespace AppBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;

class FooType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('title', TextType::class, [
                'readonly' => true,
            ]);
    }
}

This won’t work, and will give the The option "readonly" does not exist. Defined options are:... exception.

We need to set it as an attribute of that field:

src/AppBundle/Form/FooType.php

<?php
declare(strict_types=1);

namespace AppBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;

class FooType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('title', TextType::class, [
                'attr' => ['readonly' => true],
            ]);
    }
}

Happy hackin’!

Tags: form, php, symfony.
Categories: Development, Programming.

Need help on your PHP projects? Let's talk!

Waste an hour on a stupid mistake

by Robert Basic on April 07, 2017.

I made such a stupid mistake today and lost an hour of my time trying to figure out what the hell is wrong, that I just have to blog it.

I was working on embedding a Symfony form into another form, following the documentation to the letter… Yet there I was staring at this stupid error message:

The options "0", "1" do not exist. Defined options are: "action", "allow_add", "allow_delete",
"allow_extra_fields", "attr", "auto_initialize", "block_name", "by_reference", "compound",
"constraints", "csrf_field_name", "csrf_message", "csrf_protection", "csrf_token_id",
"csrf_token_manager", "data", "data_class", "delete_empty", "disabled", "empty_data",
"entry_options", "entry_type", "error_bubbling", "error_mapping", "extra_fields_message",
"help", "inherit_data", "invalid_message", "invalid_message_parameters", "label", "label_attr",
"label_format", "mapped", "method", "post_max_size_message", "property_path", "prototype",
"prototype_data", "prototype_name", "required", "translation_domain", "trim",
"upload_max_size_message", "validation_groups".

What the hell…

Excerpt of the code that was throwing the error:

<?php
declare(strict_types=1);

namespace AppBundle\Form;

use AppBundle\Form\SampleType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class MultiSampleType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('samples', CollectionType::class, [
                'entry_type', SampleType::class,
                'allow_add' => true,
            ]);
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => null,
        ));
    }
}

I read the documentation over and over again, searched for any one else coming up with the same error, went through the issues on the Symfony repositories… Nothing.

Now when I look at this code sample the error is poking my eyes out, but this morning when I was looking for it… Crickets.

Let’s “zoom” in:

<?php
->add('samples', CollectionType::class, [
    'entry_type', SampleType::class,
    'allow_add' => true,
]);

Do you see it?

The error is that the entry_type should be a key, instead of a value, and SampleType::class is the value for the entry_type key.

<?php
->add('samples', CollectionType::class, [
    'entry_type' => SampleType::class,
    'allow_add' => true,
]);

So stupid.

Oh well, I guess this also part of programming.

Happy hackin’!

Tags: php, stupid.
Categories: Blablabla, Development, Programming.

Need help on your PHP projects? Let's talk!

PHP traits to create test doubles

by Robert Basic on April 04, 2017.

Keeping your application or library code well organized, easy to follow, and read is important. Your test code should not be exempt from those rules, you should follow good testing conventions.

One part of my tests that I feel like that are out of control are the test doubles. Dummies, fakes, mocks… Seems like they are everywhere and that I keep writing the same ones over and over again.

I do follow some good practices on how to reduce code duplication in my tests, but these mocks…

Ugh.

Test doubles are everywhere

Lets look at a couple of example test cases:

tests/App/UnitTest/Transaction/TransactionTest.php

<?php declare(strict_types=1);
namespace App\UnitTest\Transaction;

use PHPUnit\Framework\TestCase;
use App\Account\Account;
use App\Account\AccountType;
use App\Transaction\Transaction;

class TransactionTest extends TestCase
{
    protected $asset;
    protected $expense;

    public function setup()
    {
        $this->asset = new Account(new AccountType('asset'), 'Cash');
        $this->expense = new Account(new AccountType('expense'), 'Groceries');
    }

    public function testTransactionCanBeExecutedBetweenAssetAndExpenseAccounts()
    {
        $transaction = new Transaction($this->asset, $this->expense, '5', 'EUR');

        $result = $transaction->execute();

        self::assertTrue($result);
    }

    public function testTransactionCannotBeExecutedBetweenExpenseAndAssetAccounts()
    {
        $transaction = new Transaction($this->expense, $this->asset, '5', 'EUR');

        $result = $transaction->execute();

        self::assertFalse($result);
    }
}

It’s not so bad, right? We create a couple of account types, so we can create a couple of account objects which are then used to test can a transaction be executed or not.

And then we need to test the persistence of the transaction in another test case. Again we create a couple of account types, accounts, create a transaction…

And then we need to test the TransactionExecuted event. Account types, accounts, transaction…

Over and over again.

Traits to the rescue

What if we move the creation of those test doubles to traits?

A trait for creating account types:

tests/Traits/AccountTypeTrait.php

<?php declare(strict_types=1);
namespace Traits;

use App\Account\AccountType;

trait AccountTypeTrait
{
    public function fakeAssetAccountType() : AccountType
    {
        return new AccountType('asset');
    }

    public function fakeExpenseAccountType() : AccountType
    {
        return new AccountType('expense');
    }
}

and a trait for creating accounts:

tests/Traits/AccountTrait.php

<?php declare(strict_types=1);
namespace Traits;

use App\Account\Account;
use Traits\AccountTypeTrait;

trait AccountTrait
{
    use AccountTypeTrait;

    public function fakeAssetAccount() : Account
    {
        return new Account($this->fakeAssetAccountType());
    }

    public function fakeExpenseAccount() : Account
    {
        return new Account($this->fakeExpenseAccountType());
    }
}

The example test case from the beginning now becomes a little bit more clear, hopefully:

tests/App/UnitTest/Transaction/TransactionTest.php

<?php declare(strict_types=1);
namespace App\UnitTest\Transaction;

use PHPUnit\Framework\TestCase;
use App\Transaction\Transaction;
use Traits\AccountTrait;

class TransactionTest extends TestCase
{
    use AccountTrait;

    public function testTransactionCanBeExecutedBetweenAssetAndExpenseAccounts()
    {
        $transaction = new Transaction($this->fakeAssetAccount(), $this->fakeExpenseAccount(), '5', 'EUR');

        $result = $transaction->execute();

        self::assertTrue($result);
    }

    public function testTransactionCannotBeExecutedBetweenExpenseAndAssetAccounts()
    {
        $transaction = new Transaction($this->fakeExpenseAccount(), $this->fakeAssetAccount(), '5', 'EUR');

        $result = $transaction->execute();

        self::assertFalse($result);
    }
}

A trait for every test double, clearly named as to what they create. fakeAssetAccount, mockTransactionRepository. Each test double can now be reused more easily across different test cases, if for some reason they need to be changed, we change them only in one place.

Just need to be disciplined on the naming of the traits and the methods they provide.

Currently I see no pitfalls with this approach, but time will tell is this a good idea or not.

Happy hackin’!

Tags: mocks, php, phpunit, tdd, test doubles, testing, traits, unit testing.
Categories: Development, Programming.

Need help on your PHP projects? Let's talk!

Loading fixtures for a Symfony app in Behat tests

by Robert Basic on March 21, 2017.

Performing end to end testing of any application requires from us to have a set of reliable test data in the database.

If we write a Symfony application and use Behat to do the end to end testing, the we can use the Doctrine fixtures bundle to create the required fixture loaders and load them in our Behat scenarios when required, using the BeforeScenario hook.

Install Doctrine fixtures bundle

Using composer we can install the Doctrine fixtures bundle:

composer require --dev doctrine/doctrine-fixtures-bundle:2.3.0

and enable the bundle in the AppKernel:

app/AppKernel.php

diff --git a/app/AppKernel.php b/app/AppKernel.php
index 0d22098..c30e863 100644
--- a/app/AppKernel.php
+++ b/app/AppKernel.php
@@ -27,6 +27,7 @@ class AppKernel extends Kernel
             $bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle();
             $bundles[] = new Sensio\Bundle\DistributionBundle\SensioDistributionBundle();
             $bundles[] = new Sensio\Bundle\GeneratorBundle\SensioGeneratorBundle();
+            $bundles[] = new Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle();
         }

         return $bundles;

Write a fixture loader

Now we can write a fixture loader. Writing fixture loaders is explained well in the official documentation.

Here’s an example fixture loader for the FOSUser bundle, creating users for the application:

src/AppBundle/DataFixtures/ORM/LoadUserData.php

<?php

namespace AppBundle\DataFixtures\ORM;

use Doctrine\Common\DataFixtures\FixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

class LoadUserData implements FixtureInterface, ContainerAwareInterface
{
    /**
     * @var ContainerInterface
     */
    private $container;

    public function setContainer(ContainerInterface $container = null)
    {
        $this->container = $container;
    }

    public function load(ObjectManager $manager)
    {
        foreach ($this->getData() as $data) {
            $userManager = $this->container->get('fos_user.user_manager');

            $user = $userManager->createUser();

            $user->setUsername($data['username']);
            $user->setUsernameCanonical($data['username']);
            $user->setPlainPassword($data['password']);

            $user->setEmail($data['email']);
            $user->setEmailCanonical($data['email']);

            $user->addRole($data['role']);
            $user->setEnabled($data['enabled']);

            $userManager->updateUser($user, true);
        }
    }

    private function getData()
    {
        return [
            [
                'username' => 'admin',
                'password' => 'adminpassword',
                'email' => 'admin@email.com',
                'firstname' => 'Boss',
                'lastname' => 'Big',
                'role' => 'ROLE_ADMIN',
                'enabled' => true,
            ],
        ];
    }
}

Do note that the LoadUserData fixture loader also implements the ContainerAwareInterface, meaning that it will get an instance of the ContainertInterface when invoked through the bin/console doctrine:fixtures:load console command.

We use this instance of the container to get the user manager from the FOSUser bundle. In turn, we use the user manager to create and update the users we are loading with this fixture loader.

Rest of it is straightforward — we set the username, email, password, role on the user and update it. The user manager will handle the password encryption as per the application configuration, saving the user to the database, and so on…

Load fixtures in Behat tests

The Behat test file has a bit more to it.

It requires the Behat Symfony2 extension (so far it works for Symfony 3 applications as well!)

composer require --dev behat/symfony2-extension:2.1.1

Next, we need to tell Behat to pass an instance of the AppKernel to our test. We do so through the behat.yml file:

behat.yml

default:
    suites:
        my_feature:
            contexts:
                - 'FeatureContext'
                    kernel: '@kernel'
    extensions:
        Behat\Symfony2Extension:
            kernel:
                class: AppKernel

This will allow our FeatureContext test file to get an instance of a KernelInterface, the AppKernel, through the constructor:

features/bootstrap/FeatureContext.php

<?php

use Doctrine\ORM\Tools\SchemaTool;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;

use AppBundle\DataFixtures\ORM\LoadUserData;
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
use Doctrine\Common\DataFixtures\Executor\ORMExecutor;
use Doctrine\Common\DataFixtures\Loader;
use Doctrine\Common\DataFixtures\Purger\ORMPurger;


class FeatureContext
{
    protected $container;

    protected $entityManager;

    public function __construct(KernelInterface $kernel)
    {
        $this->container = $kernel->getContainer();

        $this->entityManager = $this->container->get('doctrine.orm.default_entity_manager');

        $schemaTool = new SchemaTool($this->entityManager);
        $schemaTool->dropDatabase();
        $metadata = $this->entityManager->getMetadataFactory()->getAllMetadata();
        $schemaTool->createSchema($metadata);
    }
}

We get the container from the kernel and the entity manager from the container. No need to setup anything, it’s all taken care of under the hood.

We also use this “occasion” to make sure that the database schema is recreated before the test is ran.

The final step is to load the fixtures data we need in our FeatureContext.php test file:

    /**
     * @BeforeScenario
     */
    public function loadDataFixtures(BeforeScenarioScope $scope)
    {
        $userData = new LoadUserData();
        $userData->setContainer($this->container);

        $loader = new Loader();
        $loader->addFixture($userData);

        $purger = new ORMPurger();
        $purger->setPurgeMode(ORMPurger::PURGE_MODE_DELETE);

        $executor = new ORMExecutor($this->entityManager, $purger);
        $executor->execute($loader->getFixtures());
    }

The main thing to note here is that the container is not automatically set on the LoadUserData fixture loader, so we need to do that manually by calling the setContainer method with $this->container as an argument. Remember, we got the container instance from the kernel in the class constructor.

We tell the loader which fixture to load, the purger to delete any existing records from the database and finally the executor to load our fixtures.

Now when we run the Behat test suite, the database, in the environment against which we run the tests, will have a fresh set of data every time.

Happy hackin’!

Tags: behat, fixtures, php, symfony.
Categories: Development, Programming.

Need help on your PHP projects? Let's talk!