gstreamer0.10-ffmpeg
gstreamer0.10-plugins-good
packages.
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();
:
... 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:
... lines 1 - 11 | |
class MovieController extends Controller | |
{ | |
... lines 14 - 43 | |
public function listAction() | |
{ | |
$em = $this->getDoctrine()->getManager(); | |
$movies = $em->getRepository('AppBundle:Movie') | |
... lines 48 - 52 | |
} | |
} |
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:
... lines 1 - 11 | |
class MovieController extends Controller | |
{ | |
... lines 14 - 43 | |
public function listAction() | |
{ | |
$em = $this->getDoctrine()->getManager(); | |
$movies = $em->getRepository('AppBundle:Movie') | |
->findAll(); | |
return $this->render('movie/list.html.twig', array( | |
'movies' => $movies, | |
)); | |
} | |
} |
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:
{% 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.
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!
... lines 1 - 11 | |
class MovieController extends Controller | |
{ | |
... lines 14 - 43 | |
public function listAction() | |
{ | |
$em = $this->getEm(); | |
... lines 47 - 52 | |
} | |
... lines 54 - 57 | |
private function getEm() | |
{ | |
$em = $this->getDoctrine()->getManager(); | |
return $em; | |
} | |
} |
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:
... lines 1 - 11 | |
class MovieController extends Controller | |
{ | |
... lines 14 - 16 | |
public function newAction(Request $request) | |
{ | |
... lines 19 - 24 | |
$em = $this->getEm(); | |
... lines 26 - 38 | |
} | |
... lines 40 - 62 | |
} |
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:
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:
... lines 1 - 10 | |
class MovieController extends BaseController | |
{ | |
... lines 13 - 61 | |
} |
Tip
Or you can go "Code" -> "Optimize Imports" instead (Control
+ Alt
+ O
on a Mac)
to remove unused imports, add missing imports, and organize import statements in
the current file.
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
:
... 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!
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:
... lines 1 - 6 | |
class BaseController extends Controller | |
{ | |
/** | |
* @return \Doctrine\Common\Persistence\ObjectManager|object | |
*/ | |
protected function getEntityManager() | |
{ | |
$em = $this->getDoctrine()->getManager(); | |
return $em; | |
} | |
} |
... 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.
I get this errors :(
[1] Symfony\Component\Debug\Exception\ContextErrorException: Warning: count(): Parameter must be an array or an object that implements Countable
at n/a
in E:\code\symfony\phpstorm\vendor\symfony\symfony\src\Symfony\Component\Validator\Validator\RecursiveContextualValidator.php line 749
at Symfony\Component\Debug\ErrorHandler->handleError('2', 'count(): Parameter must be an array or an object that implements Countable', 'E:\code\symfony\phpstorm\vendor\symfony\symfony\src\Symfony\Component\Validator\Validator\RecursiveContextualValidator.php', '749', array('value' => object(OrderedHashMap), 'object' => object(Form), 'cacheKey' => '00000000026da31d000000000e4f5a66:children', 'metadata' => object(PropertyMetadata), 'propertyPath' => 'children', 'groups' => array('Default'), 'cascadedGroups' => null, 'traversalStrategy' => '1', 'context' => object(ExecutionContext), 'group' => 'Default', 'key' => '0', 'cascadingStrategy' => '2'))
in line
at count(null)
in E:\code\symfony\phpstorm\vendor\symfony\symfony\src\Symfony\Component\Validator\Validator\RecursiveContextualValidator.php line 749
at Symfony\Component\Validator\Validator\RecursiveContextualValidator->validateGenericNode(object(OrderedHashMap), object(Form), '00000000026da31d000000000e4f5a66:children', object(PropertyMetadata), 'children', array('Default'), null, '1', object(ExecutionContext))
in E:\code\symfony\phpstorm\vendor\symfony\symfony\src\Symfony\Component\Validator\Validator\RecursiveContextualValidator.php line 617
at Symfony\Component\Validator\Validator\RecursiveContextualValidator->validateClassNode(object(Form), '00000000026da31d000000000e4f5a66', object(ClassMetadata), '', array('Default'), null, '1', object(ExecutionContext))
in E:\code\symfony\phpstorm\vendor\symfony\symfony\src\Symfony\Component\Validator\Validator\RecursiveContextualValidator.php line 373
at Symfony\Component\Validator\Validator\RecursiveContextualValidator->validateObject(object(Form), '', array('Default'), '1', object(ExecutionContext))
in E:\code\symfony\phpstorm\vendor\symfony\symfony\src\Symfony\Component\Validator\Validator\RecursiveContextualValidator.php line 162
at Symfony\Component\Validator\Validator\RecursiveContextualValidator->validate(object(Form), null, array('Default'))
in E:\code\symfony\phpstorm\vendor\symfony\symfony\src\Symfony\Component\Validator\Validator\RecursiveValidator.php line 132
at Symfony\Component\Validator\Validator\RecursiveValidator->validate(object(Form))
in E:\code\symfony\phpstorm\vendor\symfony\symfony\src\Symfony\Component\Form\Extension\Validator\EventListener\ValidationListener.php line 68
at Symfony\Component\Form\Extension\Validator\EventListener\ValidationListener->validateForm(object(FormEvent), 'form.post_bind', object(EventDispatcher))
in line
at call_user_func(array(object(ValidationListener), 'validateForm'), object(FormEvent), 'form.post_bind', object(EventDispatcher))
in E:\code\symfony\phpstorm\app\cache\dev\classes.php line 1858
at Symfony\Component\EventDispatcher\EventDispatcher->doDispatch(array(array(object(ValidationListener), 'validateForm'), array(object(DataCollectorListener), 'postSubmit')), 'form.post_bind', object(FormEvent))
in E:\code\symfony\phpstorm\app\cache\dev\classes.php line 1773
at Symfony\Component\EventDispatcher\EventDispatcher->dispatch('form.post_bind', object(FormEvent))
in E:\code\symfony\phpstorm\vendor\symfony\symfony\src\Symfony\Component\EventDispatcher\ImmutableEventDispatcher.php line 43
at Symfony\Component\EventDispatcher\ImmutableEventDispatcher->dispatch('form.post_bind', object(FormEvent))
in E:\code\symfony\phpstorm\vendor\symfony\symfony\src\Symfony\Component\Form\Form.php line 660
at Symfony\Component\Form\Form->submit(array(), true)
in E:\code\symfony\phpstorm\vendor\symfony\symfony\src\Symfony\Component\Form\Extension\HttpFoundation\HttpFoundationRequestHandler.php line 113
at Symfony\Component\Form\Extension\HttpFoundation\HttpFoundationRequestHandler->handleRequest(object(Form), object(Request))
in E:\code\symfony\phpstorm\vendor\symfony\symfony\src\Symfony\Component\Form\Form.php line 489
at Symfony\Component\Form\Form->handleRequest(object(Request))
in E:\code\symfony\phpstorm\src\AppBundle\Controller\MovieController.php line 21
at AppBundle\Controller\MovieController->newAction(object(Request))
in line
at call_user_func_array(array(object(MovieController), 'newAction'), array(object(Request)))
in E:\code\symfony\phpstorm\vendor\symfony\symfony\src\Symfony\Component\HttpKernel\HttpKernel.php line 144
at Symfony\Component\HttpKernel\HttpKernel->handleRaw(object(Request), '1')
in E:\code\symfony\phpstorm\vendor\symfony\symfony\src\Symfony\Component\HttpKernel\HttpKernel.php line 64
at Symfony\Component\HttpKernel\HttpKernel->handle(object(Request), '1', true)
in E:\code\symfony\phpstorm\vendor\symfony\symfony\src\Symfony\Component\HttpKernel\DependencyInjection\ContainerAwareHttpKernel.php line 69
at Symfony\Component\HttpKernel\DependencyInjection\ContainerAwareHttpKernel->handle(object(Request), '1', true)
in E:\code\symfony\phpstorm\vendor\symfony\symfony\src\Symfony\Component\HttpKernel\Kernel.php line 185
at Symfony\Component\HttpKernel\Kernel->handle(object(Request))
in E:\code\symfony\phpstorm\web\app_dev.php line 28
at require('E:\code\symfony\phpstorm\web\app_dev.php')
in E:\code\symfony\phpstorm\vendor\symfony\symfony\src\Symfony\Bundle\FrameworkBundle\Resources\config\router_dev.php line 40
Hey Rizky A.
Could you give more context please. What caused that error? What Symfony and PHP version are you using?
Cheers!
if i submit form this error message show up "Warning: count(): Parameter must be an array or an object that implements Countable". How to make sure that form sending data?. I am using symfony 2.8
Hmm, I believe that error comes from an incompatible library with your current PHP version. Did you download the course code from this page?
Try updating the validator component composer up symfony/validator
Hey Jacek,
Nice tip, thank you! ;) Yeah, you can use "Code" -> "Optimize Imports" (or Control + Alt + O on a Mac) to remove unused namespaces and reorder them alphabetically.
P.S. I added a note about it here: https://github.com/knpunive...
Cheers!
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
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!
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.
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!
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!
// composer.json
{
"require": {
"php": ">=5.3.9, <7.3.0",
"symfony/symfony": "2.8.*", // v2.8.15
"doctrine/orm": "^2.4.8", // v2.4.8
"doctrine/dbal": "<2.5", // v2.4.5
"doctrine/doctrine-bundle": "~1.4", // 1.6.4
"symfony/assetic-bundle": "~2.3", // v2.8.1
"symfony/swiftmailer-bundle": "~2.3,>=2.3.10", // v2.4.2
"symfony/monolog-bundle": "^3.0.2", // v3.0.2
"sensio/distribution-bundle": "~5.0", // v5.0.17
"sensio/framework-extra-bundle": "^3.0.2", // v3.0.18
"incenteev/composer-parameter-handler": "~2.0" // v2.1.2
},
"require-dev": {
"sensio/generator-bundle": "~3.0", // v3.1.2
"symfony/phpunit-bridge": "~2.7" // v2.8.15
}
}
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 😁