Ok back to work, let's finish up this MovieController. Command+O, MovieController, and now we've got that file open.

We need to save the POST'ed movie to the database. Start with getting our entity manager with $em = $this->getDoctrine()->getManager();. Then $em->persist($movie);, and $em->flush();:

55 lines src/AppBundle/Controller/MovieController.php
... lines 1 - 11
class MovieController extends Controller
{
... lines 14 - 16
public function newAction(Request $request)
{
... lines 19 - 23
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($movie);
$em->flush();
... lines 28 - 32
}
... lines 34 - 38
}
... lines 40 - 53
}

Nice and simple!

While we're here, I'll copy the getDoctrine() line and paste it into listAction() so we can make that work. Here we'll query for the movies with $em ->getRepository() and the first cool thing here is that... you guessed it! We get autocomplete on this. Type the entity name, select the right one and hit tab:

55 lines src/AppBundle/Controller/MovieController.php
... lines 1 - 43
public function listAction()
{
$em = $this->getDoctrine()->getManager();
$movies = $em->getRepository('AppBundle:Movie')
... lines 48 - 52
}
... lines 54 - 55

The second cool thing is that it autocompletes the methods on the repository, and this includes any custom methods you have inside of there. We'll use the normal findAll();. Down below, we'll return $this->render('movies/list.html.twig'); which doesn't exist yet, with an array and we'll pass it movies:

55 lines src/AppBundle/Controller/MovieController.php
... lines 1 - 43
public function listAction()
{
$em = $this->getDoctrine()->getManager();
$movies = $em->getRepository('AppBundle:Movie')
->findAll();
return $this->render('movie/list.html.twig', array(
'movies' => $movies,
));
}
... lines 54 - 55

Awesome!

Let's go create that file: list.html.twig. And to save some time, I'll just paste in some code for this template: it's really straight forward:

20 lines app/Resources/views/movie/list.html.twig
{% extends 'base.html.twig' %}
{% block body %}
<table class="table">
<thead>
<tr>
<th>Movie name</th>
<th>Character</th>
</tr>
</thead>
<tbody>
{% for movie in movies %}
<tr>
<td>{{ movie.title }}</td>
<td>{{ movie.samsCharacterName }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

The only interesting thing here is that you do get autocomplete on the different properties for movie, Which really is pretty amazing. The reason that works is because PHPStorm knows that the MovieRepository returns Movie objects, so that cascades all the way down into Twig.

So as long as you have good PHPDoc that says what types of objects functions return, that should connect down to how things auto-complete in Twig.

Let's try this whole thing out. But before we do that we need to setup our database. Head over to parameters.yml by using our shortcut command+shift+O to open it up. Everything seems to be in order here, so let's just change our database to be phpstorm.

And just when you thought I couldn't get any lazier with my shortcuts I'm going to use the built in terminal from PHPStorm. This drops me right into the correct directory so I can run:

./app/console doctrine:database:create
./app/console doctrine:schema:create

Hide this little terminal, head back and refresh our browser. And we see... oops we have a small rendering problem, which is probably in our form template.

Back to PHPstorm, command+shift+O and search _form.html.twig. And yep there it is, change form-control to form-group - that's the correct Bootstrap class. Refresh again. That looks way better.

Now we can fill out the form. Let's see, which one of our favorite Samuel L Jackson movies should we choose first - so many choices. Let's just go with the obvious best: "Snakes on a Plane", with our character, Neville Flynn. Neville is the main character, and clearly this film was a 10 out of 10. I mean come on, this is Sam at his best! And I think everyone remembers the fateful day this film was released: August 18, 2006. Let's save this small piece of history.

Refactoring to a Method

Beautiful! Now for a little refactoring. First, we have $em = $this->getDoctrine()->getManager(); in a couple of places. So I'm going to refactor that: select that line and we'll see another important shortcut, control+t. This is the refactor shortcut. If you ever forget it, just go to the Refactor menu at the top and select "Refactor This" from the list.

We've got our line selected, press control+t. There's a lot of options in here, but we want the one called method. In the "Extract Method" form, add getEm as the name, make sure that the private radio button is selected and refactor!

64 lines src/AppBundle/Controller/MovieController.php
... lines 1 - 43
public function listAction()
{
$em = $this->getEm();
... lines 47 - 52
}
... lines 54 - 57
private function getEm()
{
$em = $this->getDoctrine()->getManager();
return $em;
}
... lines 63 - 64

Boom! It puts this on the bottom and it changes the original code to $this->getEm();. Copy this line, pressing control+c will select the whole line and we'll paste it right up here:

64 lines src/AppBundle/Controller/MovieController.php
... lines 1 - 16
public function newAction(Request $request)
{
... lines 19 - 24
$em = $this->getEm();
... lines 26 - 38
}
... lines 40 - 64

Refactoring to a BaseController

This shortcut is going to be useful in more than just this controller, so this is the perfect time to create a BaseController for my whole project. Click the Controller directory at the top of your IDE, and from there press command+n, select Controller from the menu and name it BaseController. To start, remove that public function and add abstract before the class:

9 lines src/AppBundle/Controller/BaseController.php
<?php
namespace AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class BaseController extends Controller
{
}

This is now the spot for our own shortcuts. In MovieController update it to extend BaseController. This use statement is showing up in a darker gray to indicate that it isn't being used any more and the same for the Response use statement. Delete both of those:

63 lines src/AppBundle/Controller/MovieController.php
... lines 1 - 10
class MovieController extends BaseController
{
... lines 13 - 61
}

With the BaseController in action, let's refactor getEm() into the BaseController. Select the method, one cool way to do that is hit option+up which will select larger and larger contexts. Now hit control+t. Select "Pull Members Up" from the menu, which will pull them up to the parent class. It already recognizes that BaseController is the destination, and at the bottom it's warning us that the access will be changed from private to protected, which is awesome! Hit refactor and we can see it's gone from this file, because now it lives inside of BaseController:

18 lines src/AppBundle/Controller/BaseController.php
... lines 1 - 6
class BaseController extends Controller
{
/**
* @return \Doctrine\Common\Persistence\ObjectManager|object
*/
protected function getEm()
{
$em = $this->getDoctrine()->getManager();
return $em;
}
}

That there is a really fast way to refactor things!

Refactoring to Rename a Method

Now that I have this here I'm realizing that getEm() is not that great of a name choice. So back to command+t, select rename from the menu and change it to getEntityManager to make my code a little clearer. When we do that I get a summary down here of all the spots in our project where PHPStorm sees getEm(). Click the "Do Refactor" button to rename this and all the other spots in the code that need updating:

18 lines src/AppBundle/Controller/BaseController.php
... lines 1 - 6
class BaseController extends Controller
{
/**
* @return \Doctrine\Common\Persistence\ObjectManager|object
*/
protected function getEntityManager()
{
$em = $this->getDoctrine()->getManager();
return $em;
}
}

54 lines src/AppBundle/Controller/MovieController.php
... lines 1 - 10
class MovieController extends BaseController
{
... lines 13 - 15
public function newAction(Request $request)
{
... lines 18 - 23
$em = $this->getEntityManager();
... lines 25 - 37
}
... lines 39 - 42
public function listAction()
{
$em = $this->getEntityManager();
... lines 46 - 51
}
}

Sah-weet!

There are a lot of other things you can do with the refactor feature in PHPStorm. For example, we can extract this out to a variable. This could add a level of clarity to what things are.

Or, say you get into a spot where you messed up your formatting in some terrible way. Oof that looks just awful. Make it stop! At any point, you can go up to the code menu at the top and select reformat code, which is also command+option+L and it will tidy that back up for you.

So let's hit command+A to select all the code I want to reformat, then command+option+L and now everything is back into place based on our coding standards, which you can of course control.

Leave a comment!

  • 2016-06-10 Diego Aguiar

    That's cool!
    thank you both for your time :D

  • 2016-06-10 weaverryan

    Yep, I do something similar - I use Refactor in PhpStorm... but I know it's not perfect (it never could be - there are some things that are just too dynamic for phpstorm to realize). I grep my code-base a lot to be completely sure when I make these changes. In this case, I would search for Twig instances by using:


    git grep '\.type'

    I wasn't familiar with the "Replace in Path" - that sounds very cool!

    Cheers!

  • 2016-06-10 Victor Bocharsky

    Hey, Diego!

    As for me, in this case I do "Replace in Path" (shift+command+R on any folder in project) for property name including dot in the beginning of it, i.e. .type => .format. For unique properties it helps a lot! But if property not unique, you could skip it during refactoring.

    Cheers!

  • 2016-06-10 Diego Aguiar

    Hey Ryan! how's going ?

    I've been doing some refactoring (of course, using phpstorm), and I found a problem that I just can't solve.
    When I refactor a field name from a class, lets say "type" to "format", the getter and setter method get's refactored automatically, but my problem relys on my twig templates that make use of those methods, they don't get refactored, so I have to make those changes manually.
    This is killing me D:

    Any help is very appreciated.

  • 2016-01-13 weaverryan

    Hey Matt!

    Yep, I know that error :). It means that there's a syntax error in the annotations above the samsCharacterName in your Movie entity - you might be missing a comma between your properties (e.g. @ORM\Column(name="sams_character_name", length=255).

    Btw, I like your entity+property name :)

    Cheers!

  • 2016-01-11 Matt

    Hi,

    When i attempt ./app/console doctrine:schema:create I receive this error [Doctrine\Common\Annotations\AnnotationException]

    [Syntax Error] Expected Doctrine\Common\Annotations\DocLexer::T_CLOSE_PARENTHESIS, got

    'length' at position 26 in property AppBundle\Entity\Movie::$samsCharacterName.

    doctrine:schema:create [--dump-sql] [--em [EM]]

    Probably something simple but I can't seem to work it out

  • 2015-10-06 weaverryan

    Awesome!

  • 2015-10-05 Kevin Nagurski

    Awesome set of videos, but at the end you say that you can press command a to select all and then reformat everything... with nothing selected, reformat will do the entire document. Simples. One less step 😁