public function foobar($foobar)
{
$stmt = $this->getEntityManager()
->getConnection()
->prepare('SELECT COUNT(id) AS num, foo FROM bar WHERE foobar = :foobar GROUP BY foo');
$stmt->bindValue('foobar ', $foobar);
$stmt->execute();
return $stmt->fetchAll();
}
Something like this is also doable - maybe a bit more complicated, but I like it better (notice how, via mapping, I can get "answers" as "count" and "optionname" as "option", which wouldn't work in the query itself as they're reserved words):
$rsm = new ResultSetMapping;
$rsm->addScalarResult('optionname', 'option');
$rsm->addScalarResult('answers', 'count');
return $this->_em->createNativeQuery('
SELECT
qo.title as optionname,
COUNT(qap.answer_id) as answers
FROM
quiz_answer_option qap
INNER JOIN quizoption qo ON qo.id = qap.option_id
INNER JOIN quizquestion qq ON qq.id = qo.quizquestion_id
WHERE
qq.active
AND qq.id = :quizquestionid
GROUP BY qap.option_id
ORDER BY qo.number asc
', $rsm)
->setParameter('quizquestionid', $quizquestion->getId())
->getResult();
symfony framework forum • View topic - Doctrine 2: execute raw sql?
More than 3 requests, I'll translate this to Chinese.
超过3个请求,我就会把这篇文章翻译成中文。
This is the place where I store the words when I surfing. Share with u.
这里放着我在网上看到的文章,和你一起分享。
Tuesday, July 31, 2012
Friday, July 13, 2012
Logic Exception: Symfony2 SecurityBundle and FOSUserBundle integration: How does it work?
Logic Exception: Symfony2 SecurityBundle and FOSUserBundle integration: How does it work?
app/config/security.yml
app/config/routing.yml
vendor/bundles/FOS/UserBundle/Resources/config/routing/security.xml
vendor/bundles/FOS/UserBundle/Controller/SecurityController.php
In the security controller, we have both check and logout action methods included but they are not defined. Yet, the security process works. How is this possible?
Let's go back to the beginning; everything starts when SecurityBundle is initiated and our security factory definitions are loaded from security_factories.xml.
Symfony/Bundle/SecurityBundle/Resources/config/security_factories.xml
Basically, the call to load the SecurityBundle (see SecurityExtension::load()) simply calls the SecurityExtension::createFirewalls() method which in turn makes a call to the SecurityExtension::createListenerFactories() method. This method then loads all services tagged with "security.listener.factory" in the security_factories.xml file which is included above.
Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
Listener factories are classes that are responsible for initiating listeners based on the authentication type. Symfony SecurityBundle provides factories for multiple authentication types. These authentication types include HTTP Basic, HTTP Digest, X509, RememberMe, and FormLogin. In our case, we are interested in the FormLogin type.
Once all listener factories are loaded, the code then loops over the firewall definitions in our app/config/security.yml file and calls SecurityExtension::createFirewall() method for each one found. In our app/config/config.yml file, we have three firewalls defined: main, dev, and login.
Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
Subsequently, the SecurityExtension::createFirewall() method initiates authentication listeners for each particular firewall by calling the SecurityExtension::createAuthenticationListeners() method.
Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
The SecurityExtension::createAuthenticationListeners() method identifies the correct factory classes for a firewall by simply looping over all factories loaded into memory previously in a specific order (see SecurityBundle::$listenerPositions property and getPosition() methods inside factory classes) and then matching the keys found under the firewall definition (in our case, we have pattern, form_login, logout, and anonymous keys under the main firewall) to a factory instance (see getKey() methods inside factory classes). At the end, there is only one factory class defined with the form_login key and it is located at Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FormLoginFactory.php.
Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
Note: SecurityExtension::createAuthenticationListeners() mehod also creates a listener for anonymous authentication. Code is excluded for the sake of brevity.
Once an appropriate factory class is identified, the FormLoginFactory::create() method is called. This registers a "security.authentication.listener.form" authentication listener that intercepts login attempts.
The FormLoginFactory::create() method also calls createAuthProvider() which is responsible for registering our custom user provider (fos_userbundle). This user provider class is used to verify login credentials against a database.
The FormLoginFactory class also declares security.authentication.listener.form as our listener id. (See FormLoginFactory::getListenerId())
Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.xml
As you can see in the security_listeners.xml, our listener class is Symfony\Component\Security\Http\Firewall\UsernamePasswordFormAuthenticationListener. Things get easier after this point. Any call to our UsernamePasswordFormAuthenticationListener::handle() method triggers attemptAuthentication() method. This method is where the interaction between FOSUserBundle and SecurityBundle begins.
First, the authenticate() method is called on our authentication provider, which then makes a call to our user provider. This is the UserManager class included in the FOSUserBundle package.
Symfony/Component/Security/Http/Firewall/UsernamePasswordFormAuthenticationListener.php
Then, based on return value of the authenticate() call, onSuccess() or onFailure() method is called. If user login is a success, the AbstractAuthenticationListener::onSuccess() method dispatches an event identfied by the SecurityEvents::INTERACTIVE_LOGIN constant.
Symfony/Component/Security/Http/Firewall/AbstractAuthenticationListener.php
SecurityEvents::INTERACTIVE_LOGIN constant has a value of security.interactive_login so we can easily locate where the login listener registration happens:
vendor/bundles/FOS/UserBundle/Resources/config/security.xml
And here is the custom listener code attached to the security.interactive_login event. It is purpose is to update the last login date for our users after a successful login.
FOS/UserBundle/Security/InteractiveLoginListener.php
In my next post, I will illustrate how we can extend this behavior to implement additional functionality after a successful login.
More than 3 requests, I'll translate this to Chinese.
超过3个请求,我就会把这篇文章翻译成中文。
Symfony2 SecurityBundle and FOSUserBundle integration: How does it work?
OVERVIEW
A couple of days ago, I realized I needed to add some new functionality to the login process. Specifically, I needed to track all previous login attempts. Not knowing anything about the new Symfony2 SecurityBundle, I had to go through the underlying code to understand what was going on. In the process, I think got a basic idea about how the new SecurityBundle interacts with FOSUserBundle.CONFIGURATION
I have a basic security configuration as illustrated below.app/config/security.yml
security: encoders: Symfony\Component\Security\Core\User\User: plaintext role_hierarchy: ROLE_ADMIN: ROLE_USER ROLE_SUPER_ADMIN: ROLE_ADMIN providers: fos_userbundle: id: fos_user.user_manager firewalls: main: pattern: .* form_login: provider: fos_userbundle check_path: /user/login_check login_path: /user/login logout: path: /user/logout anonymous: true dev: pattern: ^/(_(profiler|wdt)|css|images|js)/ security: false login: pattern: ^/user/login$ security: falseFull security configuration reference can be found at here
app/config/routing.yml
fos_user_security: resource: "@FOSUserBundle/Resources/config/routing/security.xml" prefix: /userAs you can see above, we are importing all FOSUserBundle security routing rules with the /user prefix. Now, let's have a look at the security routing rules under FOSUserBundle.
vendor/bundles/FOS/UserBundle/Resources/config/routing/security.xml
It should be pretty self explanatory up to this point. We have a default security routing config file that we are importing into our application with the /user prefix and we have all the matching paths (login_path, check_path, etc.) defined in our app/config/config.yml for a working FOSUserBundle implementation. Now, let's have a look at FOSUserBundle SecurityController.FOSUserBundle:Security:login FOSUserBundle:Security:check FOSUserBundle:Security:logout
vendor/bundles/FOS/UserBundle/Controller/SecurityController.php
- namespace FOS\UserBundle\Controller;
- use Symfony\Component\DependencyInjection\ContainerAware;
- use Symfony\Component\Security\Core\SecurityContext;
- use Symfony\Component\Security\Core\Exception\AuthenticationException;
- class SecurityController extends ContainerAware
- {
- public function loginAction()
- {
- $request = $this->container->get('request');
- /* @var $request \Symfony\Component\HttpFoundation\Request */
- $session = $request->getSession();
- /* @var $session \Symfony\Component\HttpFoundation\Session */
- // get the error if any (works with forward and redirect -- see below)
- if ($request->attributes->has(SecurityContext::AUTHENTICATION_ERROR)) {
- $error = $request->attributes->get(SecurityContext::AUTHENTICATION_ERROR);
- } elseif (null !== $session && $session->has(SecurityContext::AUTHENTICATION_ERROR)) {
- $error = $session->get(SecurityContext::AUTHENTICATION_ERROR);
- $session->remove(SecurityContext::AUTHENTICATION_ERROR);
- } else {
- $error = '';
- }
- if ($error) {
- // TODO: this is a potential security risk (see http://trac.symfony-project.org/ticket/9523)
- $error = $error->getMessage();
- }
- // last username entered by the user
- $lastUsername = (null === $session) ? '' : $session->get(SecurityContext::LAST_USERNAME);
- return $this->container->get('templating')->renderResponse('FOSUserBundle:Security:login.html.'.$this->container->getParameter('fos_user.template.engine'), array(
- 'last_username' => $lastUsername,
- 'error' => $error,
- ));
- }
- public function checkAction()
- {
- throw new \RuntimeException('You must configure the check path to be handled by the firewall using form_login in your security firewall configuration.');
- }
- public function logoutAction()
- {
- throw new \RuntimeException('You must activate the logout in your security firewall configuration.');
- }
- }
HOW DOES IT WORK?
Well, a single word: listeners.- First, an appropriate listener factory class is identified based on the authentication type.
- Second, this listener factory class registers a user provider identified by the provider key under the firewall definition in security.yml. In our case, our provider is called fos_userbundle. This process basically links SecurityBundle to FOSUserBundle for authenticating submitted from values against a database.
- Next, the listener factory class registers an authentication listener identified by "security.authentication.listener.form". This code listens to login attempts at the /user/login path.
- Finally, FOSUserBundle registers a login listener for security.interactive_login event. This event indicates a successful login and the listener is used to execute post login code.
Let's go back to the beginning; everything starts when SecurityBundle is initiated and our security factory definitions are loaded from security_factories.xml.
Symfony/Bundle/SecurityBundle/Resources/config/security_factories.xml
- xml version="1.0" ?>
- <container xmlns="http://symfony.com/schema/dic/services"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
- <services>
- <service id="security.authentication.factory.form" class="Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\FormLoginFactory">
- <tag name="security.listener.factory" />
- service>
- <service id="security.authentication.factory.x509" class="Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\X509Factory">
- <tag name="security.listener.factory" />
- service>
- <service id="security.authentication.factory.basic" class="Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\HttpBasicFactory">
- <tag name="security.listener.factory" />
- service>
- <service id="security.authentication.factory.digest" class="Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\HttpDigestFactory">
- <tag name="security.listener.factory" />
- service>
- <service id="security.authentication.factory.remember_me" class="Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\RememberMeFactory">
- <tag name="security.listener.factory" />
- service>
- services>
- container>
Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
- private function createFirewalls($config, ContainerBuilder $container)
- {
- // ...
- // create security listener factories
- $factories = $this->createListenerFactories($container, $config);
- // ...
- }
Once all listener factories are loaded, the code then loops over the firewall definitions in our app/config/security.yml file and calls SecurityExtension::createFirewall() method for each one found. In our app/config/config.yml file, we have three firewalls defined: main, dev, and login.
Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
- private function createFirewalls($config, ContainerBuilder $container)
- {
- // ...
- foreach ($firewalls as $name => $firewall) {
- list($matcher, $listeners, $exceptionListener) = $this->createFirewall($container, $name, $firewall, $authenticationProviders, $providerIds, $factories);
- $contextId = 'security.firewall.map.context.'.$name;
- $context = $container->setDefinition($contextId, new DefinitionDecorator('security.firewall.context'));
- $context
- ->replaceArgument(0, $listeners)
- ->replaceArgument(1, $exceptionListener)
- ;
- $map[$contextId] = $matcher;
- }
- // ...
- }
Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
- private function createFirewall(ContainerBuilder $container, $id, $firewall, &$authenticationProviders, $providerIds, array $factories)
- {
- // ...
- // Authentication listeners
- list($authListeners, $defaultEntryPoint) = $this->createAuthenticationListeners($container, $id, $firewall, $authenticationProviders, $defaultProvider, $factories);
- // ...
- }
Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
- private function createAuthenticationListeners($container, $id, $firewall, &$authenticationProviders, $defaultProvider, array $factories)
- {
- // ...
- foreach ($this->listenerPositions as $position) {
- foreach ($factories[$position] as $factory) {
- $key = str_replace('-', '_', $factory->getKey());
- if (isset($firewall[$key])) {
- $userProvider = isset($firewall[$key]['provider']) ? $this->getUserProviderId($firewall[$key]['provider']) : $defaultProvider;
- list($provider, $listenerId, $defaultEntryPoint) = $factory->create($container, $id, $firewall[$key], $userProvider, $defaultEntryPoint);
- $listeners[] = new Reference($listenerId);
- $authenticationProviders[] = $provider;
- $hasListeners = true;
- }
- }
- }
- // ...
- }
Once an appropriate factory class is identified, the FormLoginFactory::create() method is called. This registers a "security.authentication.listener.form" authentication listener that intercepts login attempts.
The FormLoginFactory::create() method also calls createAuthProvider() which is responsible for registering our custom user provider (fos_userbundle). This user provider class is used to verify login credentials against a database.
The FormLoginFactory class also declares security.authentication.listener.form as our listener id. (See FormLoginFactory::getListenerId())
Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.xml
- xml version="1.0" ?>
- <container xmlns="http://symfony.com/schema/dic/services"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
- <parameters>
- <parameter key="security.authentication.listener.form.class">Symfony\Component\Security\Http\Firewall\UsernamePasswordFormAuthenticationListenerparameter>
- parameters>
- <services>
- <service id="security.authentication.listener.form"
- class="%security.authentication.listener.form.class%"
- parent="security.authentication.listener.abstract"
- abstract="true">
- service>
- services>
- container>
First, the authenticate() method is called on our authentication provider, which then makes a call to our user provider. This is the UserManager class included in the FOSUserBundle package.
Symfony/Component/Security/Http/Firewall/UsernamePasswordFormAuthenticationListener.php
- protected function attemptAuthentication(Request $request)
- {
- // ...
- return $this->authenticationManager->authenticate(new UsernamePasswordToken($username, $password, $this->providerKey));
- }
Symfony/Component/Security/Http/Firewall/AbstractAuthenticationListener.php
- private function onSuccess(GetResponseEvent $event, Request $request, TokenInterface $token)
- {
- // ...
- if (null !== $this->dispatcher) {
- $loginEvent = new InteractiveLoginEvent($request, $token);
- $this->dispatcher->dispatch(SecurityEvents::INTERACTIVE_LOGIN, $loginEvent);
- }
- // ...
- }
vendor/bundles/FOS/UserBundle/Resources/config/security.xml
- xml version="1.0" encoding="UTF-8"?>
- <container xmlns="http://symfony.com/schema/dic/services"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
- <parameters>
- <parameter key="fos_user.encoder_factory.class">FOS\UserBundle\Security\Encoder\EncoderFactoryparameter>
- <parameter key="fos_user.security.interactive_login_listener.class">FOS\UserBundle\Security\InteractiveLoginListenerparameter>
- parameters>
- <services>
- <service id="fos_user.encoder_factory" class="%fos_user.encoder_factory.class%" public="false">
- <argument>%security.encoder.digest.class%argument>
- <argument>%fos_user.encoder.encode_as_base64%argument>
- <argument>%fos_user.encoder.iterations%argument>
- <argument type="service" id="fos_user.encoder_factory.parent" />
- service>
- <service id="fos_user.security.interactive_login_listener" class="%fos_user.security.interactive_login_listener.class%">
- <argument type="service" id="fos_user.user_manager" />
- <tag name="kernel.event_listener" event="security.interactive_login" method="onSecurityInteractiveLogin" />
- service>
- services>
- container>
FOS/UserBundle/Security/InteractiveLoginListener.php
- namespace FOS\UserBundle\Security;
- use FOS\UserBundle\Model\UserManagerInterface;
- use FOS\UserBundle\Model\UserInterface;
- use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
- use DateTime;
- class InteractiveLoginListener
- {
- protected $userManager;
- public function __construct(UserManagerInterface $userManager)
- {
- $this->userManager = $userManager;
- }
- public function onSecurityInteractiveLogin(InteractiveLoginEvent $event)
- {
- $user = $event->getAuthenticationToken()->getUser();
- if ($user instanceof UserInterface) {
- $user->setLastLogin(new DateTime());
- $this->userManager->updateUser($user);
- }
- }
- }
More than 3 requests, I'll translate this to Chinese.
超过3个请求,我就会把这篇文章翻译成中文。
Subscribe to:
Posts (Atom)