Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Create Genus Note

Video not working?

It looks like your browser may not support the H264 codec. If you're using Linux, try a different browser or try installing the gstreamer0.10-ffmpeg gstreamer0.10-plugins-good packages.

Thanks! This saves us from needing to use Flash or encode videos in multiple formats. And that let's us get back to making more videos :). But as always, please feel free to message us.

It's you again! Welcome back friend! In this tutorial, we're diving back into Doctrine: this time to master database relations. And to have fun of course - databases are super fun.

Like usual, you should code along with me or risk 7 years of bad luck. Sorry. To do that, download the code from the course page and use the start directory. I already have the code - so I'll startup our fancy, built-in web server:

./bin/console server:run

You may also need to run composer install and a few other tasks. Check the README in the download for those details.

When you're ready, pull up the genus list page at http://localhost:8000/genus. Nice!

Create the GenusNote Entity

Click to view a specific genus. See these notes down here? These are loaded via a ReactJS app that talks to our app like an API. But, the notes themselves are still hardcoded right in a controller. Bummer! Time to make them dynamic - time to create a second database table to store genus notes.

To do that, create a new GenusNote class in the Entity directory. Copy the ORM use statement from Genus that all entities need and paste it here:

... lines 1 - 2
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
... lines 6 - 10
class GenusNote
{
... lines 13 - 78
}

With that, open the "Code"->"Generate" menu - or Cmd + N on a Mac - and select "ORM Class":

... lines 1 - 2
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
* @ORM\Table(name="genus_note")
*/
class GenusNote
{
... lines 13 - 78
}

Bam! This is now an entity!

Next: add the properties we need. Let's see... we need a username, an userAvatarFilename, notes and a createdAt property:

... lines 1 - 10
class GenusNote
{
... lines 13 - 17
private $id;
... lines 19 - 22
private $username;
... lines 24 - 27
private $userAvatarFilename;
... lines 29 - 32
private $note;
... lines 34 - 37
private $createdAt;
... lines 39 - 78
}

When we add a user table later - we'll replace username with a relationship to that table. But for now, keep it simple.

Open the "Code"->"Generate" menu again and select "ORM Annotation". Make sure each field type looks right. Hmm, we probably want to change $note to be a text type - that type can hold a lot more than the normal 255 characters:

... lines 1 - 10
class GenusNote
{
/**
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string")
*/
private $username;
/**
* @ORM\Column(type="string")
*/
private $userAvatarFilename;
/**
* @ORM\Column(type="text")
*/
private $note;
/**
* @ORM\Column(type="datetime")
*/
private $createdAt;
... lines 39 - 78
}

Finally, go back to our best friend - the "Code"->"Generate" menu - and generate the getter and setters for every field - except for id. You don't usually want to set the id, but generate a getter for it:

... lines 1 - 10
class GenusNote
{
... lines 13 - 39
public function getId()
{
return $this->id;
}
public function getUsername()
{
return $this->username;
}
public function setUsername($username)
{
$this->username = $username;
}
public function getUserAvatarFilename()
{
return $this->userAvatarFilename;
}
public function setUserAvatarFilename($userAvatarFilename)
{
$this->userAvatarFilename = $userAvatarFilename;
}
public function getNote()
{
return $this->note;
}
public function setNote($note)
{
$this->note = $note;
}
public function getCreatedAt()
{
return $this->createdAt;
}
public function setCreatedAt($createdAt)
{
$this->createdAt = $createdAt;
}
}

Generate Migrations

Entity done! Well, almost done - we still need to somehow relate each GenusNote to a Genus. We'll handle that in a second.

But first, don't forget to generate a migration for the new table:

./bin/console doctrine:migrations:diff

Open up that file to make sure it looks right - it lives in app/DoctrineMigrations. CREATE TABLE genus_note - it looks great! Head back to the console and run the migration:

./bin/console doctrine:migrations:migrate

Adding Fixtures

Man, that was easy. We'll want some good dummy notes too. Open up the fixtures.yml file and add a new section for AppBundle\Entity\GenusNote. Start just like before: genus.note_ and - let's create 100 notes - so use 1..100:

... lines 1 - 8
AppBundle\Entity\GenusNote:
genus.note_{1..100}:
... lines 11 - 15

Next, fill in each property using the Faker functions: username: <username()> and then userAvatarFilename: Ok, eventually users might upload their own avatars, but for now, we have two hardcoded options: leanna.jpeg and ryan.jpeg. Let's select one of these randomly with a sweet syntax: 50%? leanna.jpeg : ryan.jpeg. That's Alice awesomeness:

... lines 1 - 7
AppBundle\Entity\GenusNote:
genus.note_{1..100}:
username: <userName()>
userAvatarFilename: '50%? leanna.jpeg : ryan.jpeg'
... lines 13 - 15

The rest are easy: note: <paragraph()> and createdAt: <dateTimeBetween('-6 months', 'now')>:

... lines 1 - 8
AppBundle\Entity\GenusNote:
genus.note_{1..100}:
username: <userName()>
userAvatarFilename: '50%? leanna.jpeg : ryan.jpeg'
note: <paragraph()>
createdAt: <dateTimeBetween('-6 months', 'now')>

Ok, run the fixtures!

./bin/console doctrine:fixtures:load

Double-check them with a query:

./bin/console doctrine:query:sql 'SELECT * FROM genus_note'

So awesome! Ok team, we have two entities: let's add a relationship!

Leave a comment!

50
Login or Register to join the conversation
Default user avatar

Why aren't you using fluent setters?

1 Reply

Hey Mike!

Ah, no reason in particular :). I'm *super* accustomed (just from years of habit) to not having fluent setters. I also rarely (but sometimes) call multiple setters in a row. But I definitely don't see any issue with fluent setters!

Cheers!

Reply

there

I just noticed, I have a couple of little things that you could integrate into your files to help the viewers in the long run.

create a symlink for bin/console
ln -s bin/console console
add an alias for the console command
(be it on .aliases or your .bash_rc file)
alias console="php console"

type "console"

See the results here http://goo.gl/n1P4CU

oh my terminal? well thats powerline-shell -> https://github.com/milkbiki... <- at work (linux here, works in the mac too for those interested)

also I notice you use php bin/console server:run and not server:start ? any particular reason for it?

Reply

I like this stuff - good tips!

And no reason about server:start versus server:run. I feel like new people may have less trouble with server:run, as they may accidentally keep server:start running in the background, then come back later, try to start it again, and get an error.

Cheers!

1 Reply
Default user avatar
Default user avatar Luka Sikic | posted 4 years ago

Hi Ryan,

I wonder isn't it a better practice to use bin/console doctrine:generate:entity ?

Reply

Hey Luka Sikic

I'm not sure if there is a best practice for it, but for teaching is better to show how you can create a new entity from scratch
Anyways, feel free to create them however you please, as long as you don't introduce a bug :)

Cheers!

Reply
Default user avatar

Hi how can I connect to a MSSQL database with doctrine please?

Reply

Hey Henry,

In Symfony project: just change "database_driver" to "pdo_sqlsrv" instead of "pdo_mysql" in your "app/config/parameters.yml". For custom project based on Doctrine - find a place where configuration parameters are setting according to the Doctrine configuration.

P.S. And also don't forget to tweak other DB credentials as host, port, username and password.

Cheers!

Reply
Default user avatar

Hi,guys
after load bin/console doctrine:fixtures:load
unexpectedvalueexception could not determine how to assign createAt to a AppBundle\Entity\GenusNote object

Reply
Default user avatar

It's ok evrything is workin

Reply

Hey Ilya,

Great! I think the problem was that you used `createAt` instead of `createdAt`, right? It should be `createdAt`. Your explanation could help other devs with similar problem.

Cheers!

Reply
Pascal A. Avatar

Hi Guys,

I had the same problem. For me I put a 's' at the end of the class 'GenusNote' => 'GenusNotes'. So It was wrong in the fixtures.yml file.

I have to say many THX for this course. I follow every Symfony 3 lesson. Very very good way to understand this framework before to start à Big project.

Reply

Hey Pascal,

Thanks for your feedback! We are really happy you like it.

Cheers!

Reply
Richard Avatar
Richard Avatar Richard | posted 4 years ago

In the introductory text it might be an idea to say whether our existing project (from the end of fixture data) is sufficient to begin this course.

Reply

Ooh, good tip! In reality, it IS ok to use the existing project... unless I say otherwise (occasionally I make changes between courses, and I explicitly say to use the download code). But, I definitely do not make that crystal clear! We will make this more clear going forward!

Reply
Default user avatar
Default user avatar Dan Costinel | posted 4 years ago

Hi KNP!

I'm facing a problem and I can't find a solution, even though I'm following (at least I guess I do) the right "rules" for defining a ManyToMany relation (Category - Filters)
I want to be able to add a list of filters for a specific category, and vice-versa, a list of categories for a specific Filter.
For that I have to following code:


# AppBundle/Entity/Category.php
#...
/**
* @ORM\ManyToMany(targetEntity="Filter", inversedBy="categories")
*/
private $filters;

public function __construct() {
$this->filters = new \Doctrine\Common\Collections\ArrayCollection();
}

public function addFilter(\AppBundle\Entity\Filter $filter) {
$this->filters[] = $filter;
}

public function removeFilter(\AppBundle\Entity\Filter $filter) {
$this->filters->removeElement($filter);
}

public function getFilters() {
return $this->filters;
}


# AppBundle/Entity/Filter.php
#...
/**
* @ORM\ManyToMany(targetEntity="Category", mappedBy="filters")
*/
public $categories;

public function __construct()
{
$this->categories = new \Doctrine\Common\Collections\ArrayCollection();
}

public function addCategory(\AppBundle\Entity\Category $category) {
$category->addFilter($this);
$this->categories[] = $category;
}

public function removeCategory(\AppBundle\Entity\Category $category) {
$this->categories->removeElement($category);
}


public function getCategories() {
return $this->categories;
}

For adding filters to a category, I have this form:


# AppBundle/Entity/CategoryType
#...
->add('filters', EntityType::class, [
'class' => 'AppBundle:Filter',
'placeholder' => ' ',
'query_builder' => function(EntityRepository $er) {
return $er->createQueryBuilder('f');
},
'choice_label' => function($filters) {
return $filters->getValue();
},
'multiple' => false,
'expanded' => false
])

For adding categories to a filter, I have this form:


->add('categories', EntityType::class, [
'class' => 'AppBundle:Category',
'placeholder' => ' ',
'query_builder' => function(EntityRepository $er) {
return $er->createQueryBuilder('c');
},
'choice_label' => function($categories) {
return $categories->getName();
},
'multiple' => false,
'expanded' => false
])

When I render any of the two forms, all things goes ok, no problems here.
Questions:

1. how can I save, for the first time, information in any of the two tables? Because, if I want to add a filter for a category, then the dropdown for this first category is empty - as there are no filters yet. And vice-versa, if I want to add a category for a filter, the dropdown containing categories is empty - as there are no categories yet.

2. if I comment out the ->add('categories') from AppBundleForm/FilterType, and I try to add a new category and a filter for it, then I get this error:


Error 500: Could not determine access type for property "filters".

Any ideas on how to solve this two issues?

Thank you!

Reply

Yo Dan Costinel!

Ok, let's do this! First, your setup looks cool - both relationships are setup correctly. In our collections tutorial (which goes into ManyToMany specifically), we do a bit more work in the adder method (you can see an example here: http://knpuniversity.com/sc... to make sure that we can't ever add duplicate links. This isn't your problem, just something to be aware of. Also, adding categories to your filter should work pretty easily, but adding filters to your category will mean that you are setting things from the *inverse* side of the relationship. So, you'll need to do a bit more work with that. But no worries, all stuff we talk about :) http://knpuniversity.com/sc...

Now, let me answer your questions specifically

> 1) how can I save, for the first time, information in any of the two tables?

It depends on your requirements! Should the system start in an empty state? If so, then at some point, the user will need to create a Filter with no Categories or a Category with no Filters in order to start things off. But in many cases, your system might always start with a certain set of Categories (or Filters). In that case, I would add those in your fixtures.

> 2) If I comment out the ->add('categories') from AppBundleForm/FilterType, and I try to add a new category and a filter for it, then I get this error: ...

I think *this* is unrelated to uncommenting ->add('categories') - I think uncommenting that simply allows for your form to submit far enough to hit the error. The error seems to be saying that you have a "filters" form field on your FilterType! Because this is the error you get when a field exists on your form, but no getter/setter/adder/remover exists on the class to handle this. So, double-check the fields you have on your FilterType!

I hope this gets you a step closer!

Cheers!

Reply
Default user avatar

Thanks for replying Ryan, kind as always!

1) Yeah, the system should start as an empty one. And I was kinda sure about the right answer, which is to choose one of the sides, and create some data for it, and then add data for the other entity. But I asked just to be sure!

2). I create a new test project in symfony, and retyped all again, and still the problem persists, as I get the same error: "Error 500: Could not determine access type for property "filters".", even though I'm not declaring any "filters" form type in my FilterType.php file. Here's a gist of my entire test project: https://gist.github.com/dan... .

Firstly, I thought this might be caused because I'm not adding any filter to a category, using the addFilter($category->getFilters()) Category class method. But then I realised the error is thrown previously to the submission (at least this is what I suppose), because it complains at the line which contains $form->handleRequest($request); (here's the print of the error: http://imgur.com/a/f4WsC )

If you are able to see why this is happening, that would be great!

Thank you!

Reply

Yo Dan!

1) Awesome :)

2) Thanks for the code and screenshot! And you're right - this happens when the form is submitted. And I can see from your screenshot that the form component is trying to set a "filters" field on a Category object. So, even though you say this is happening when you submit the FilterType form, it is (somehow) your CategoryType that is causing the problem. Somehow, the CategoryType is being submitted, or is embedded in your form. And actually, I can see this in your exception screenshot - the categoryAction is being executed - not filterAction!

Apart from that, even if your CategoryType *is* being submitted, it *should* in theory work: after all, you *do* have addFilter, removeFilter and getFilters() in Category. I'm honestly not sure what the issue is here - but I would try to add (or uncomment in your case) the by_reference option on your filters field in CategoryType.

I hope this helps find the issue! Cheers!

Reply
Default user avatar

Hi Ryan.
I think I finally found the problem. It seems that the problem is with that 'multiple' => false option. If it is set to true then the whole process works, and only when is set to false throws that error.

Ok, done! But then I'm facing a new problem.
My select is no more a default select, but this: http://imgur.com/a/j2BF2 .

Any tip on how to make this one look like an usual select?

Thanks!

Reply

Hey Dan!

Sorry for the late reply! I wanted to reply 2 days ago... but AWS S3 was down, and so was Imgur! But now, I can see your screenshot :).

First, happy the multiple worked! Because your Category is allowed to have an *array* of filters, this makes sense! I'm not sure why I missed that!

Second, because the Category is allowed to have many filters... it doesn't really make sense to use a normal drop-down select anymore. That's why Symfony is rendering it in this way. If this were a normal drop-down, then you would only be able to choose one filter per Category. But, I also hate this "multi-select" - very not user friendly. If you want to use checkboxes instead, then add an 'expanded' => true option to the field. If you want to do something even *fancier* - like some sort of jQuery widget - then you'll probably want to keep the field as it is now, and then point that jQuery widget at the multi-select. Usually, these widgets work by hiding the true element (i.e. the multi-select) and then displaying the fancy widget. But, behind the scenes, when you choose things on the widget, it updates the multi-select.

Let me know if that helps! And cheers!

Reply
Default user avatar

Hi Ryan.
No problem!
Indeed, I solved the problem regarding the aspect of the drop-down, using "selectize" js plugin.
Thanks for support!

Reply
Default user avatar
Default user avatar jk_scotland | posted 4 years ago

When I load the fixtures I get this :

johnk@johnk-OptiPlex-XE2 /media/johnk/73F9A5F807B5266F/workspace/DoctrineRels $ ./bin/console doctrine:fixtures:load
Careful, database will be purged. Do you want to continue y/N ?y
> purging database
> loading AppBundle\DataFixtures\ORM\LoadFixtures

[Symfony\Component\Debug\Exception\FatalThrowableError]
Call to a member function format() on string

Unfortunately a search of the workspace for format gives over 10,000 results - is there any way of getting more info on where the actual error is located?

Reply

Hey jk_scotland!

That's weird, if you want to check the full message, you can pipe everything into a file, something like this (in linux)
$ bin/console d:fix:load --no-interaction > result.txt

Anyways, that error look's like something easy to fix. Can you show me your fixtures.yml?

Cheers!

Reply
Default user avatar

Found that there was a -v switch that indicated that it was a dateTime problem

Exception trace:
() at /media/johnk/73F9A5F807B5266F/workspace/DoctrineRels/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateTimeType.php:53

And at your suggestion looked in fixtures.yml and found the '>' was missing as the end of 'createdAt: <datetimebetween('-6 months',="" 'now')="">'

Reply

Ohh, so it is working now? :)

Reply
Default user avatar

Hello Ryan.

How to enable ORM Annotation on code generate menu? In my menu this option inactive.

Thanks.

Reply

Hi again!

Hmm, is it inactive, or do you *not* see the option at all? Make sure your cursor is *inside* your entity class when you use the Code->Generate menu.

Let me know what you find out :)

Reply
Default user avatar

I do you *not* see the option at all.

Reply

Ah, make sure you have the PHP Annotations plugin installed for PhpStorm. In the first course, I did *not* mention this - we're making an edit to the video to add this missing item!

Reply
Default user avatar

thanks, problem solved :)

Reply
Default user avatar
Default user avatar Nobuyuki Fujioka | posted 4 years ago

Just a quick question, in your video, when you create getter and setter for a parameter in GenusNote class, phpStorm is creating getter and setter functions without annotations. But, with my PHPstorm, it is creating annotation for each one.
Below is an example:
```
/**
* @return mixed
*/
public function getUsername()
{
return $this->username;
}

/**
* @param mixed $username
*/
public function setUsername($username)
{
$this->username = $username;
}
```
What are these
/**
* @return mixed
*/
and
/**
* @param mixed $username
*/
?

Do we need them?

Cheers,
Noby

Reply

Hey Nobuyuki,

My PhpStorm creates setters and getters with annotation too, so probably Ryan has different settings, or just an older version of PhpStorm, or it just was a bug in PhpStorm :)

This annotation called PHP DocBlock comments. You can read more about it here: https://phpdoc.org/docs/lat... . Basically, this annotations helps IDEs like PhpStorm to do autocompletion. Usually, it's unnecessary for IDE because it can resolve returned type base on @var property annotation. The types of method arguments can be resolved base on typehint, but it don't work for scalar type, that's why @param annotation for methods with scalar values would be good here. But nothing critical won't happen without these annotations for getters/setters, you just don't have an autocompletion in some places of your code.

Actually, here's my recent merged PR to the Symfony Demo project about annotation: https://github.com/symfony/... . I think you might be interested in it - there's a small discussion about annotation best practice.

Cheers!

1 Reply
Default user avatar
Default user avatar Nobuyuki Fujioka | victor | posted 4 years ago

Hi, Victor

Thank you very much. I understand it better now thanks to your explanation.

Kind regards,
Noby

Reply

You're welcome!

Cheers!

Reply
Default user avatar
Default user avatar Maksym Minenko | posted 4 years ago

Out of 100 usernames three are exactly the same. Is there an easy way to make them unique?

Reply

Yo Maksym,

The <current()> function return a current index of each iteration. Combine it with username to make it 100% unique.

Cheers!

Reply
Default user avatar
Default user avatar Maksym Minenko | victor | posted 4 years ago

Do you mean like this, for example?
username: <username()>_<current()>

Well, not the most elegant solution, but... it works. :) I guess it's quite ok for development purposes.

Reply

Yes, agree, but I think that's the simplest solution. BTW, I just use username: user<current()>.

Reply
Default user avatar
Default user avatar Maksym Minenko | victor | posted 4 years ago

Ryan would say: "But that's so boooring" :D

Ok, ok, it's quite suffice for me, thank you! ;)

Reply

haha, yes, it's :D

Reply
Default user avatar
Default user avatar Nobuyuki Fujioka | posted 4 years ago

Hi,

When I create an entity class, can I have different names for id like below?
(@ORM\Id and private $connectorId)
/**
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
* @ORM\Column(type="integer")
*/
private $connectorId;

Cheers,
Noby

Reply

Hey Nobuyuki,

I think you can. Have you tried it? Btw, what about just map $id field to the different column name in database:


/**
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
* @ORM\Column(name="connector_id", type="integer")
*/
private $id;


This should work as well.

Cheers!

Reply
Default user avatar
Default user avatar Nobuyuki Fujioka | victor | posted 4 years ago

Hi, Victor
Thank you for your reply. My first try seemed to have worked. But, will see.
I will try your solution as well.

I have many other questions. so, will ask you later.

Cheers,
Noby

Reply
Default user avatar

Is there a reason for the Faker fixtures.yml to use "genus.note_{1..100}" instead of "genus_note_{1..100}" or "genusNote_{1..100}" (or in fact "anything_at_all_{1..100}" ?

Clearly it doesn't use this value for a field in the SQL- is it just for an internal Faker ID thing? Thanks!

Reply

Hey Goz,

You're right, there's no any difference here, you can use whatever string you want, but of course, it's good when it's relevant to avoid confusing other developers and you later :) Just make sure this key is unique across all your Alice fixtures to be able to refer those objects.

Cheers!

Reply
Default user avatar
Default user avatar Julia Shishik | posted 4 years ago

Good morning! Can you help me! when i do 'doctrine:migrations:migrate' I have error and create new table 'Migrating up to 20170421064206 from 0

++ migrating 20170418203924

-> CREATE TABLE genus (id INT AUTO_INCREMENT NOT N....' 'SQLSTATE[42S01]: Base table or view already exists: 1050 Table 'genus' already exists...'

'[Doctrine\DBAL\Driver\PDOException]
SQLSTATE[42S01]: Base table or view alrea
dy exists: 1050 Table 'genus' already exi
sts

[PDOException]
SQLSTATE[42S01]: Base table or view alrea
dy exists: 1050 Table 'genus' already exi
sts...'

Reply

Hey Julia,

Well, the reason is simple: as you can see from the error message you already have the `genus` table in your DB, but your migration's trying to create this table. So most likely you're doing something wrong. I find it useful to read the docs for DoctrineMigrationsBundle to understand how it works, some base concepts of migrations and what this bundle provides to you out of the box: https://symfony.com/doc/cur...

In shorts, the main question is *why* this `genus` table already exist on the time you run this migration at the first time? Probably you added this table with `bin/console doctrine:schema:create` or `bin/console doctrine:schema:update --force` command. But if you decided to control your DB with migrations, you have to forget about these create/update DB commands unless you have understanding of how migrations work at least.

For now, to fix this error easy, you can try to remove your DB at all with `bin/console doctrine:database:drop --force`, then create only the DB with `bin/console doctrine:database:create` and after these actions you can start using migrations.

Does it help to fix your error?

Cheers!

Reply
Default user avatar
Default user avatar Julia Shishik | victor | posted 4 years ago

Yes! All good! You did it in the next lesson!
bin/console doctrine:database:drop --force
bin/console doctrine:database:create
It helped! Thank you
I try to learn! Excuse me!

Reply

Hey Julia,

Great! I'm glad we're on the same wave. And no problem! Actually, it's good that you ask questions even quicker than we explain them in the course, well done!
If you have any questions, do not hesitate to ask again ;)

Cheers!

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=5.5.9",
        "symfony/symfony": "3.1.*", // v3.1.4
        "doctrine/orm": "^2.5", // v2.7.2
        "doctrine/doctrine-bundle": "^1.6", // 1.6.4
        "doctrine/doctrine-cache-bundle": "^1.2", // 1.3.0
        "symfony/swiftmailer-bundle": "^2.3", // v2.3.11
        "symfony/monolog-bundle": "^2.8", // 2.11.1
        "symfony/polyfill-apcu": "^1.0", // v1.2.0
        "sensio/distribution-bundle": "^5.0", // v5.0.22
        "sensio/framework-extra-bundle": "^3.0.2", // v3.0.16
        "incenteev/composer-parameter-handler": "^2.0", // v2.1.2
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "knplabs/knp-markdown-bundle": "^1.4", // 1.4.2
        "doctrine/doctrine-migrations-bundle": "^1.1" // 1.1.1
    },
    "require-dev": {
        "sensio/generator-bundle": "^3.0", // v3.0.7
        "symfony/phpunit-bridge": "^3.0", // v3.1.3
        "nelmio/alice": "^2.1", // 2.1.4
        "doctrine/doctrine-fixtures-bundle": "^2.3" // 2.3.0
    }
}