gstreamer0.10-ffmpeg
gstreamer0.10-plugins-good
packages.
Time to build a login form. And guess what? This page is no different than every other page: we'll create a route, a controller and render a template.
For organization, create a new class called SecurityController
. Extend the normal
Symfony base Controller
and add a public function loginAction()
:
... lines 1 - 2 | |
namespace AppBundle\Controller; | |
use Symfony\Bundle\FrameworkBundle\Controller\Controller; | |
... lines 6 - 7 | |
class SecurityController extends Controller | |
{ | |
... lines 10 - 12 | |
public function loginAction() | |
{ | |
} | |
} |
Setup the URL to be /login
and call the route security_login
:
... lines 1 - 5 | |
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; | |
class SecurityController extends Controller | |
{ | |
/** | |
* @Route("/login", name="security_login") | |
*/ | |
public function loginAction() | |
{ | |
} | |
} |
Make sure to auto-complete the @Route
annotation so you get the use
statement
up top.
Cool!
Every login form looks about the same, so let's go steal some code. Google for "Symfony security form login" and Find a page called How to Build a Traditional Login Form.
Find their loginAction()
, copy its code and paste it into ours:
... lines 1 - 7 | |
class SecurityController extends Controller | |
{ | |
... lines 10 - 12 | |
public function loginAction() | |
{ | |
$authenticationUtils = $this->get('security.authentication_utils'); | |
// get the login error if there is one | |
$error = $authenticationUtils->getLastAuthenticationError(); | |
// last username entered by the user | |
$lastUsername = $authenticationUtils->getLastUsername(); | |
return $this->render( | |
'security/login.html.twig', | |
array( | |
// last username entered by the user | |
'last_username' => $lastUsername, | |
'error' => $error, | |
) | |
); | |
} | |
} |
Notice, one thing is immediately weird: there's no form processing code inside of here. Welcome to the strangest part of Symfony's security. We will build the login form here, but some other magic layer will actually handle the form submit. We'll build that layer next.
But thanks to this handy security.authentication_utils
service, we can at least
grab any authentication error that may have just happened in that magic layer as
well as the last username that was typed in, which will actually be an email address
for us.
To create the template, hit Option+enter on a Mac and select the option to create the template. Or you can go create this by hand.
You guys know what to do: add {% extends 'base.html.twig' %}
. Then, override
{% block body %}
and add {% endblock %}
. I'll setup some markup to get us started:
{% extends 'base.html.twig' %} | |
{% block body %} | |
<div class="container"> | |
<div class="row"> | |
<div class="col-xs-12"> | |
<h1>Login!</h1> | |
... lines 8 - 27 | |
</div> | |
</div> | |
</div> | |
{% endblock %} |
Great! This template also has a bunch of boilerplate code, so copy that from the
docs too. Paste it here. Update the form action route to security_login
:
... lines 1 - 2 | |
{% block body %} | |
<div class="container"> | |
<div class="row"> | |
<div class="col-xs-12"> | |
<h1>Login!</h1> | |
{% if error %} | |
<div>{{ error.messageKey|trans(error.messageData, 'security') }}</div> | |
{% endif %} | |
<form action="{{ path('security_login') }}" method="post"> | |
<label for="username">Username:</label> | |
<input type="text" id="username" name="_username" value="{{ last_username }}" /> | |
<label for="password">Password:</label> | |
<input type="password" id="password" name="_password" /> | |
{# | |
If you want to control the URL the user | |
is redirected to on success (more details below) | |
<input type="hidden" name="_target_path" value="/account" /> | |
#} | |
<button type="submit">login</button> | |
</form> | |
</div> | |
</div> | |
</div> | |
{% endblock %} |
Well, it ain't fancy, but let's try it out: go to /login
. There it is, in all its
ugly glory.
Now, I bet you've noticed something else weird: we are not using the form system: we're building the HTML form by hand. And this is totally ok. Security is strange because we will not handle the form submit in the normal way. Because of that, most people simply build the form by hand: you can do it either way.
But... our form is ugly. And I know from our forms course, that the form system is already setup to render using Bootstrap-friendly markup. So if we did use a real form... this would instantly be less ugly.
So let's do that: in the Form
directory, create a new form class called LoginForm
.
Remove getName()
- that's not needed in Symfony 3 - and configureOptions()
:
... lines 1 - 2 | |
namespace AppBundle\Form; | |
use Symfony\Component\Form\AbstractType; | |
... line 6 | |
use Symfony\Component\Form\FormBuilderInterface; | |
... lines 8 - 9 | |
class LoginForm extends AbstractType | |
{ | |
public function buildForm(FormBuilderInterface $builder, array $options) | |
{ | |
... lines 14 - 17 | |
} | |
} |
This is a rare time when I won't bother binding my form to a class.
Tip
If you're building a login form that will be used with Symfony's native form_login
system, override getBlockPrefix()
and make it return an empty string. This will
put the POST data in the proper place so the form_login
system can find it.
In buildForm()
, let's add two things, _username
and _password
, which should be
a PasswordType
:
... lines 1 - 5 | |
use Symfony\Component\Form\Extension\Core\Type\PasswordType; | |
... lines 7 - 9 | |
class LoginForm extends AbstractType | |
{ | |
public function buildForm(FormBuilderInterface $builder, array $options) | |
{ | |
$builder | |
->add('_username') | |
->add('_password', PasswordType::class) | |
; | |
} | |
} |
You can name these fields anything, but _username
and _password
are common in the
Symfony world. Again, we're calling this _username
, but for us, it's an email.
Next, open SecurityController
and add $form = $this->createForm(LoginForm::class)
:
... lines 1 - 6 | |
use AppBundle\Form\LoginForm; | |
class SecurityController extends Controller | |
{ | |
... lines 11 - 13 | |
public function loginAction() | |
{ | |
... lines 16 - 23 | |
$form = $this->createForm(LoginForm::class, [ | |
... line 25 | |
]); | |
... lines 27 - 34 | |
} | |
} |
And, if the user just failed login, we need to pre-populate their _username
field. To pass the form default data, add a second argument: an array with _username
set to $lastUsername
:
... lines 1 - 23 | |
$form = $this->createForm(LoginForm::class, [ | |
'_username' => $lastUsername, | |
]); | |
... lines 27 - 36 |
Finally, skip the form processing: that will live somewhere else. Pass the form
into the template, replacing $lastUsername
with 'form' => $form->createView()
:
... lines 1 - 8 | |
class SecurityController extends Controller | |
{ | |
... lines 11 - 13 | |
public function loginAction() | |
{ | |
... lines 16 - 27 | |
return $this->render( | |
'security/login.html.twig', | |
array( | |
'form' => $form->createView(), | |
'error' => $error, | |
) | |
); | |
} | |
} |
Open up the template, Before we get to rendering, make sure our eventual error message
looks nice. Add alert alert-danger
:
... lines 1 - 2 | |
{% block body %} | |
<div class="container"> | |
<div class="row"> | |
<div class="col-xs-12"> | |
<h1>Login!</h1> | |
{% if error %} | |
<div class="alert alert-danger"> | |
{{ error.messageKey|trans(error.messageData, 'security') }} | |
</div> | |
{% endif %} | |
... lines 14 - 19 | |
</div> | |
</div> | |
</div> | |
{% endblock %} |
Now, kill the entire form and replace it with our normal form stuff: form_start(form)
,
from_end(form)
, form_row(form._username)
and form_row(form._password)
:
... lines 1 - 2 | |
{% block body %} | |
<div class="container"> | |
<div class="row"> | |
<div class="col-xs-12"> | |
<h1>Login!</h1> | |
{% if error %} | |
<div class="alert alert-danger"> | |
{{ error.messageKey|trans(error.messageData, 'security') }} | |
</div> | |
{% endif %} | |
{{ form_start(form) }} | |
{{ form_row(form._username) }} | |
{{ form_row(form._password) }} | |
... line 18 | |
{{ form_end(form) }} | |
</div> | |
</div> | |
</div> | |
{% endblock %} |
Don't forget your button! type="submit"
, add a few classes, say Login
and get
fancy with a lock icon:
... lines 1 - 2 | |
{% block body %} | |
<div class="container"> | |
<div class="row"> | |
<div class="col-xs-12"> | |
... lines 7 - 14 | |
{{ form_start(form) }} | |
... lines 16 - 17 | |
<button type="submit" class="btn btn-success">Login <span class="fa fa-lock"></span></button> | |
{{ form_end(form) }} | |
</div> | |
</div> | |
</div> | |
{% endblock %} |
We did this purely so that Ryan could get his form looking less ugly. Let's see if it worked. So much better!
Oh, while we're here, let's hook up the Login
button on the upper right. This lives
in base.html.twig
. The login form is just a normal route, so add path('security_login')
:
<html> | |
... lines 3 - 13 | |
<body> | |
... lines 15 - 19 | |
<header class="header"> | |
... lines 21 - 22 | |
<ul class="navi"> | |
... line 24 | |
<li><a href="{{ path('security_login') }}">Login</a></li> | |
</ul> | |
</header> | |
... lines 28 - 46 | |
</body> | |
</html> |
Refresh, click that link, and here we are.
Login form complete. It's finally time for the meat of authentication: it's time to build an authenticator.
Hey Richard ,
What do you mean? You may find all the code in this screencast below the video in course script. Our code blocks are expandable, it means you can click on double arrow against the line to load more context of the file. Or you can download the whole course project code in up right corner: "Download" -> "Course Code" if you want to see the final code to the end of this course - but it's available for users who one the course or have paid subscription only. Does it makes sense for you?
Cheers!
No. As you typed the bootstrap code it auto completed. Some sort of downloadable template for Idea editor?
Hey Richard
I believe you are talking about "live templates", if you are using PHPStorm, you can create your own templates by going to: settings > editor > live templates
You can read more info here: https://www.jetbrains.com/h...
Have a nice day
Hey Richard ,
Yea, we have one, check it out here: https://github.com/knpunive... . But as Diego said, you can create your owns very easy. Check out our screencast about PhpStorm to get more info about how to do it: https://knpuniversity.com/s...
Cheers!
Hey Ponrad,
Good question! Yes, it's a good practice. And Symfony docs recommend to do this to protect your login form against CSRF attacks: http://symfony.com/doc/curr...
Cheers!
Error " Variable "form" does not exist in security\login.html.twig at line 11"
Controller Security
namespace Admin\AdminBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Admin\AdminBundle\Form\LoginForm;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
class SecurityController extends Controller
{
/**
* @Route("/login", name="security_login")
*/
public function loginAction()
{
$authenticationUtils = $this->get('security.authentication_utils');
$error = $authenticationUtils->getLastAuthenticationError();
// Ия пользователя
$lastUsername = $authenticationUtils->getLastUsername();
$form = $this->createForm(LoginForm::class, [
'_username' => $lastUsername,
]);
return $this->render(
'security/login.html.twig',
array(
'last_username' => $lastUsername,
'error' => $error,
)
);
}
}
LoginForm Class
namespace Admin\AdminBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
class LoginForm extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('_username')
->add('_password', PasswordType::class);
}
}
login.html.twig
{% extends 'base.html.twig' %}
{% block body %}
<div class="row">
<div class="small-12 medium-12 large-12 columns">
<h2>Login</h2>
{% if error %}
<div>{{ error.messageKey|trans(error.messageDate, 'security') }}</div>
{% endif %}
{{ form_start(form) }}
{{ form_widget(form) }}
{{ form_row(form._username) }}
{{ form_row(form._password) }}
<button type="submit">login</button>
{{ form_end(form) }}
</div>
</div>
{% endblock %}
Hey Andrey,
What's your question here? :)
I think you probably forget to pass $form to the template, here's a fix:
return $this->render('security/login.html.twig', array(
'last_username' => $lastUsername,
'error' => $error,
'form' => $form->createView(),
));
P.S. Don't forget to call "createView()" method on form object before passing it to the template.
Cheers!
AppBundle\Controller\:
resource: '../../src/AppBundle/Controller'
public: true
tags: ['controller.service_arguments']
All things are same as u mentioned but still i am getting error following error
Controller
"EmployeeBundle\Controller\webLoginController::loginAction()" requires
that you provide a value for the "$authUtils" argument. Either the
argument is nullable and no null value has been provided, no default
value has been provided or because there is a non optional argument
after this one.
Iam using symfony 3.3.5V
Hey Asimsagir Abbasi
Could you show me your controller's code? So, I can help you debugging.
Cheers!
:) but i am facing new prblm ,i am always receiving invalid credentials . i am using my own EmployeeBundle
pls let me know why this happening
following is my security.yml file codes:
security:
encoders:
EmployeeBundle\Entity\EmployeeLogin:
algorithm: bcrypt
# https://symfony.com/doc/current/security.html#b-configuring-how-users-are-loaded
providers:
db_provider:
entity:
class: EmployeeBundle:EmployeeLogin
property: userName
firewalls:
# disables authentication for assets and the profiler, adapt it according to your needs
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: ~
form_login:
login_path: login
check_path: login
This is my controller:
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
class webLoginController extends Controller
{
/**
* @Route("/login",name="login")
*/
public function loginAction(Request $request,AuthenticationUtils $authUtils)
{
// get the login error if there is one
$error = $authUtils->getLastAuthenticationError();
// last username entered by the user
$lastUsername = $authUtils->getLastUsername();
return $this->render('EmployeeBundle:webLogin:login.html.twig', array(
'last_username' => $lastUsername,
'error' => $error,
));
}
}
And this is my login form:
<form action="{{ path('login') }}" method="post">
<label for="userName">Username:</label>
<input type="text" id="userName" name="_userName" value="{{ last_username }}" />
<label for="password">Password:</label>
<input type="password" id="password" name="_password" />
{#
If you want to control the URL the user
is redirected to on success (more details below)
<input type="hidden" name="_target_path" value="/account" />
#}
<button type="submit">login</button>
</form>
Hey Asimsagir,
Input fields names are important here, they should be "_username" and "_password". Please, notice the letter case for username: "_username" not "_userName", etc.. I think that's the problem.
Cheers!
This no longer works with the demo code from the symfony site, you need to copy it from the script or the downloads finish folder.
Yo Robert Went!
You're right that you can always copy from the finish folder :). But, I *am* curious which part changed - was it the login template? Or the login controller? I *think* I know the issue (and it's my fault - I updated the documentation on Symfony.com): In Symfony 3.3, you can fetch services in a new way (we're talking about it here: http://knpuniversity.com/sc.... The Symfony.com documentation has been updated to use this... which is cool! Except if you're using a Symfony 3.2 project... and then you try to copy from the 3.3 version of the docs (which show up by default). If I'm right, then it was the controller code that was the problem. Was that it?
Oh, also, for other people, we do have the code on each page below the video - so if you ever want to copy a bit of code, you can grab it there really quickly.
Cheers!
Ah yeah, that makes sense.
It was the controller code for the login action from the 3.3 version which shows up as default
public function loginAction(Request $request, AuthenticationUtils $authUtils)```
$request isn't defined.
Ah, I was actually expending an error with the $autUtils
part! :) As long as you have the use
statement for the Request
, that should actually pass you the request (in any Symfony version). But anyways... there is a legit difference in 3.3 - so I'm glad you asked!
Cheers!
Its not the request part :
public function loginAction(Request $request, AuthenticationUtils $authUtils)
Its the authenticationUtils parameter. That doesnt work. Interestingly the symfony docs go from:
public function loginAction(Request $request)
to
public function loginAction(Request $request, AuthenticationUtils $authUtils)
without any explanation. Is this some sort for dependency injection perhaps or "auto something or other" that saves us having to query the container?
Hey Max,
Yes, this auto-injection feature by typehint in actions allows do not use container directly, i.e. fetch services from the container, and as a consequence, you can make more your services private, what is cool.
Cheers!
Hey Ryan,
Actually I got an error with the updated code from Symfony doc; I'm running 3.3.2 so I thought I'd try it :)
Controller "AdminBundle\Controller\SecurityController::loginAction()" requires that you provide a value for the "$authUtils" argument. Either the argument is nullable and no null value has been provided, no default value has been provided or because there is a non optional argument after this one.
There isn't much on the official doc to help me figure it out; I know I'm supposed to pass the $authUtils argument but what is it exactly? :p
Hey Edouard PERRIN!
Hmm, let's see if we can find your problem... and maybe also improve the docs at the same time :).
Do you have the use
statement for the AuthenticationUtils
class? It would be:
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
If you do have that, can you post your loginAction
exactly? Also, is this a new project or did you upgrade from an older version of Symfony? What does your services.yml file look like?
Cheers!
Hey weaverryan,
Ok, so I thought it was working but trying the new code again, it still doesn't work.
Everything works with the old way btw.
I have the use statement; I upgraded mid-project from Symfony 3.something to 3.3.2
Services.yml
https://pastebin.com/RsTi71bZ
Security.yml
https://pastebin.com/ztPu8ZPR
SecurityController
https://pastebin.com/340jchpb
LoginForm
https://pastebin.com/3tBc5Wy0
LoginFormAuthenticator
https://pastebin.com/nSMfwJR6
Yo Edouard PERRIN!
Ah, ok, I've got it! In order for the "service controller argument" thing to work (http://knpuniversity.com/sc..., your controller must be registered as a service. Basically, when you upgrade from Symfony 3.2 to 3.3, *if* you want to use all of the new features, you need some config changes in services.yml. We talk about this in the 3.3 tutorial: http://knpuniversity.com/sc.... Most notably, you need this bit of configuration: http://knpuniversity.com/sc.... In fact, I think you could add just those lines (I'm talking about the first code block on that page - lines 23-28) and that would do it :).
Cheers!
Nice catch weaverryan!! That was the right move, it works smoothly now. Guess I better take a few moments to watch the 3.3 tutorial :)
Thanks for your help!
I'm getting the same issue, using 3.3.0.
Controller "AppBundle\Controller\SecurityController::LoginAction()" requires that you provide a value for the "$authUtils" argument. Either the argument is nullable and no null value has been provided, no default value has been provided or because there is a non optional argument after this one.
Even though I do have the controllers added in the services.yml.
AppBundle\Controller\:
resource: '../../src/AppBundle/Controller'
public: true
tags: ['controller.service_arguments']
And I have the use statement:
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
However, when I was using basically the same code in a project that was 3.3.4 (i think?) it worked without a hiccup. I'm going to walk away and come back, because it seems like something I'm missing...unless the minor version makes a difference here.
That said, it does work if I just use:
$authUtils = $this->get('security.authentication_utils');
Hey Geoff,
Yep, it should work for public services well, but if you have private one but can't get that controller's argument injection worked - you need to manually make your services public. Thanks for sharing this workaround for others!
Cheers!
Hey Geoff.
Hm, it should work for 3.3.0. I'm not 100% sure but probably you need to add:
services:
# default configuration for services in *this* file
_defaults:
# automatically registers your services as commands, event subscribers, etc.
autoconfigure: true
I think it should help, just don't forget to clear the cache after this change. If it does not help, could you make sure that your Symfony dependencies are locked at least at those versions: https://github.com/symfony/symfony-standard/blob/3.3/composer.json#L16-L28 - I suppose it's not enough to just have symfony/symfony 3.3.0.
Cheers!
Yeah, not sure what's up. As per my last comment, i tried 3.3.4 with this project and it made no difference. So it must be another config issue. I do have that already set in services.yml
services:
# default configuration for services in *this* file
_defaults:
# automatically injects dependencies in your services
autowire: true
# automatically registers your services as commands, event subscribers, etc.
autoconfigure: true
# this means you cannot fetch services directly from the container via $container->get()
# if you need to do this, you can override this setting on individual services
public: false
# makes classes in src/AppBundle available to be used as services
# this creates a service per class whose id is the fully-qualified class name
AppBundle\:
resource: '../../src/AppBundle/*'
# you can exclude directories or files
# but if a service is unused, it's removed anyway
exclude: '../../src/AppBundle/{Entity,Repository,Resources}'
# controllers are imported separately to make sure they're public
# and have a tag that allows actions to type-hint services
AppBundle\Controller\:
resource: '../../src/AppBundle/Controller'
public: true
tags: ['controller.service_arguments']
An additional followup here...I tried to update the project to 3.3.4, and it's still throwing the same error. So it's not the symfony version. Not sure what else to try to get this to work injecting the AuthenticationUtils class.
The weird thing is in my OTHER project that is on 3.3.4 and has basically the same code, the services.yml files are almost identical (the only differences are unrelated services).
Hey geoff,
I see the latest version of Symfony is v3.3.11 in that branch. Why didn't you update the project to the latest one? And btw, how did you update it? what steps did you use? Tweaked "symfony/symfony" version to "3.3.4" in composer.json and ran exactly "composer update" command?
Cheers!
It's possible some context of my issue got lost here because it's in different comments - I updated to 3.3.4 specifically because injecting AuthenticationUtils is working in another very similar project I have (it was a test project I started with most of the same code). However, when I updated in this project, it made no difference. I could try 3.3.11, but I don't think that's the issue.
More context: Another similar project that I have that is on 3.3.4 - so that's why I tried it.
OK, great. But what about your way of updating dependencies? Did you tweak your dependencies versions to match these minor versions: https://github.com/symfony/... ? Also, you need to run "composer update" to upgrade all the packages, not only "symfony/symfony" one.
Cheers!
As a test, I injected the class into the constructor rather than directly into the loginAction, and that worked. Any thought on why that would be the case?
class SecurityController extends Controller
{
private $authUtils;
public function __construct(AuthenticationUtils $authenticationUtils)
{
$this->authUtils = $authenticationUtils;
}
/**
* @param Request $request
* @return \Symfony\Component\HttpFoundation\Response
*/
public function loginAction(Request $request)
{
// TODO fix injection: should be able to inject AuthenticationUtils into this action, but it's throwing an error unless i inject into constructor
//$authUtils = $this->get('security.authentication_utils');
// get the login error if there is one
$error = $this->authUtils->getLastAuthenticationError();
...
Hm, ok, then 2 questions:
Probably somehow it does not match criteria of this filter:
services:
AppBundle\Controller\:
resource: '../../src/AppBundle/Controller'
and registers as a service.
Cheers!
Thanks for following up on this. Unfortunately I don't really see an issue there.
services.yml
Path: project\app\config\services.yml
SecurityController.php
namespace AppBundle\Controller;
...
Path: project\src\AppBundle\Controller\SecurityController.php
Hey Geoff Maddock
Can you try something for me? Create a new action method in that controller and inject any other service, let's see if that works
I had the same issue.
public function loginAction() {
// instead of using dependency injection fill the variable straight with get() method.
$authUtils = $this->get('security.authentication_utils');
// get the login error if there is one
$error = ........
+1 this code (as we do it in the tutorial) is safe in all versions of Symfony :). There is a different way to do it also in Symfony 3.3 - but if you're not sure, just use this way - it's totally great!
Thanks for the comment!
SF 3.3.6 doesn't have getName() after form creation but it does have getBlockPrefix(). Should this function be removed as well?
Hey Mike P.
Yeah, feel free to remove it from your Form class, it is used to name the Form element and the default value is really good.
From docs: The block prefix defaults to the underscored short class name with the "Type" suffix removed (e.g. "UserProfileType" => "user_profile").
Cheers!
there hi,
Yip it's1 me again, Got a question about phpStorm.
So 1. the ACTION Live Template I got and created but..
2. The "options -> Create template" shortcut key ??
First I'm on windows 10 pro, yip I know bad misstake I know but Mac book pro is just out of budget reach.. you get.
I did do one thing correct I bought phpStorm v2017.3.4
Am I missing a plugin ?
// 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
}
}
How did you complete that loginAction and bootrow?