diff --git a/.travis.yml b/.travis.yml index c20155875c2..9afcdfaaff4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,6 @@ python: - "2.7" install: - - "git submodule update --init" - "bash install.sh" - "pip install -q -r requirements.txt --use-mirrors" diff --git a/book/installation.rst b/book/installation.rst index 7c5c80560e9..d6908290464 100644 --- a/book/installation.rst +++ b/book/installation.rst @@ -233,7 +233,7 @@ If there are any issues, correct them now before moving on. $ rm -rf app/cache/* $ rm -rf app/logs/* - $ APACHEUSER=`ps aux | grep -E '[a]pache|[h]ttpd|[_]www|[w]ww-data' | grep -v root | head -1 | cut -d\ -f1` + $ APACHEUSER=`ps aux | grep -E '[a]pache|[h]ttpd|[_]www|[w]ww-data|[n]ginx' | grep -v root | head -1 | cut -d\ -f1` $ sudo chmod +a "$APACHEUSER allow delete,write,append,file_inherit,directory_inherit" app/cache app/logs $ sudo chmod +a "`whoami` allow delete,write,append,file_inherit,directory_inherit" app/cache app/logs @@ -248,7 +248,7 @@ If there are any issues, correct them now before moving on. .. code-block:: bash - $ APACHEUSER=`ps aux | grep -E '[a]pache|[h]ttpd|[_]www|[w]ww-data' | grep -v root | head -1 | cut -d\ -f1` + $ APACHEUSER=`ps aux | grep -E '[a]pache|[h]ttpd|[_]www|[w]ww-data|[n]ginx' | grep -v root | head -1 | cut -d\ -f1` $ sudo setfacl -Rn -m u:"$APACHEUSER":rwX -m u:`whoami`:rwX app/cache app/logs $ sudo setfacl -dRn -m u:"$APACHEUSER":rwX -m u:`whoami`:rwX app/cache app/logs diff --git a/cookbook/console/commands_as_services.rst b/cookbook/console/commands_as_services.rst new file mode 100644 index 00000000000..c6001e246bf --- /dev/null +++ b/cookbook/console/commands_as_services.rst @@ -0,0 +1,122 @@ +.. index:: + single: Console; Commands as Services + +How to Define Commands as Services +================================== + +.. versionadded:: 2.4 + Support for registering commands in the service container was introduced in + version 2.4. + +By default, Symfony will take a look in the ``Command`` directory of each +bundle and automatically register your commands. If a command extends the +:class:`Symfony\\Bundle\\FrameworkBundle\\Command\\ContainerAwareCommand`, +Symfony will even inject the container. +While making life easier, this has some limitations: + +* Your command must live in the ``Command`` directory; +* There's no way to conditionally register your service based on the environment + or availability of some dependencies; +* You can't access the container in the ``configure()`` method (because + ``setContainer`` hasn't been called yet); +* You can't use the same class to create many commands (i.e. each with + different configuration). + +To solve these problems, you can register your command as a service and tag it +with ``console.command``: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/config.yml + services: + acme_hello.command.my_command: + class: Acme\HelloBundle\Command\MyCommand + tags: + - { name: console.command } + + .. code-block:: xml + + + + + + + + + + + + + .. code-block:: php + + // app/config/config.php + $container + ->register('acme_hello.command.my_command', 'Acme\HelloBundle\Command\MyCommand') + ->addTag('console.command') + ; + +Using Dependencies and Parameters to Set Default Values for Options +------------------------------------------------------------------- + +Imagine you want to provide a default value for the ``name`` option. You could +pass one of the following as the 5th argument of ``addOption()``: + +* a hardcoded string; +* a container parameter (e.g. something from parameters.yml); +* a value computed by a service (e.g. a repository). + +By extending ``ContainerAwareCommand``, only the first is possible, because you +can't access the container inside the ``configure()`` method. Instead, inject +any parameter or service you need into the constructor. For example, suppose you +have some ``NameRepository`` service that you'll use to get your default value:: + + // src/Acme/DemoBundle/Command/GreetCommand.php + namespace Acme\DemoBundle\Command; + + use Acme\DemoBundle\Entity\NameRepository; + use Symfony\Component\Console\Command\Command; + use Symfony\Component\Console\Input\InputInterface; + use Symfony\Component\Console\Input\InputOption; + use Symfony\Component\Console\Output\OutputInterface; + + class GreetCommand extends Command + { + protected $nameRepository; + + public function __construct(NameRepository $nameRepository) + { + $this->nameRepository = $nameRepository; + } + + protected function configure() + { + $defaultName = $this->nameRepository->findLastOne(); + + $this + ->setName('demo:greet') + ->setDescription('Greet someone') + ->addOption('name', '-n', InputOption::VALUE_REQUIRED, 'Who do you want to greet?', $defaultName) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $name = $input->getOption('name'); + + $output->writeln($name); + } + } + +Now, just update the arguments of your service configuration like normal to +inject the ``NameRepository``. Great, you now have a dynamic default value! + +.. caution:: + + Be careful not to actually do any work in ``configure`` (e.g. make database + queries), as your code will be run, even if you're using the console to + execute a different command. diff --git a/cookbook/console/console_command.rst b/cookbook/console/console_command.rst index fc903b12650..30ea6f0d9cf 100644 --- a/cookbook/console/console_command.rst +++ b/cookbook/console/console_command.rst @@ -65,55 +65,11 @@ This command will now automatically be available to run: .. _cookbook-console-dic: Register Commands in the Service Container ------------------------------------------- - -.. versionadded:: 2.4 - Support for registering commands in the service container was added in - version 2.4. - -Instead of putting your command in the ``Command`` directory and having Symfony -auto-discover it for you, you can register commands in the service container -using the ``console.command`` tag: - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/config.yml - services: - acme_hello.command.my_command: - class: Acme\HelloBundle\Command\MyCommand - tags: - - { name: console.command } - - .. code-block:: xml - - - - - - - - - - - .. code-block:: php - - // app/config/config.php - - $container - ->register('acme_hello.command.my_command', 'Acme\HelloBundle\Command\MyCommand') - ->addTag('console.command') - ; - -.. tip:: +------------------------------------------- - Registering your command as a service gives you more control over its - location and the services that are injected into it. But, there are no - functional advantages, so you don't need to register your command as a service. +Just like controllers, commands can be declared as services. See the +:doc:`dedicated cookbook entry ` +for details. Getting Services from the Service Container ------------------------------------------- diff --git a/cookbook/console/index.rst b/cookbook/console/index.rst index 878d1fc862a..6f1f939d772 100644 --- a/cookbook/console/index.rst +++ b/cookbook/console/index.rst @@ -8,3 +8,4 @@ Console usage sending_emails logging + commands_as_services diff --git a/cookbook/form/dynamic_form_modification.rst b/cookbook/form/dynamic_form_modification.rst index 5c966d5b2eb..c100fb52210 100644 --- a/cookbook/form/dynamic_form_modification.rst +++ b/cookbook/form/dynamic_form_modification.rst @@ -99,7 +99,7 @@ creating that particular field is delegated to an event listener:: { $builder->add('price'); - $builder->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event) { + $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) { // ... adding the name field if needed }); } @@ -116,7 +116,7 @@ the event listener might look like the following:: public function buildForm(FormBuilderInterface $builder, array $options) { // ... - $builder->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event) { + $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) { $product = $event->getData(); $form = $event->getForm(); @@ -249,7 +249,7 @@ Using an event listener, your form might look like this:: ->add('subject', 'text') ->add('body', 'textarea') ; - $builder->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event) { + $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) { // ... add a choice list of friends of the current application user }); } @@ -325,13 +325,13 @@ and fill in the listener logic:: $builder->addEventListener( FormEvents::PRE_SET_DATA, - function(FormEvent $event) use ($user) { + function (FormEvent $event) use ($user) { $form = $event->getForm(); $formOptions = array( 'class' => 'Acme\DemoBundle\Entity\User', 'property' => 'fullName', - 'query_builder' => function(EntityRepository $er) use ($user) { + 'query_builder' => function (EntityRepository $er) use ($user) { // build a custom query // return $er->createQueryBuilder('u')->addOrderBy('fullName', 'DESC'); @@ -491,7 +491,7 @@ sport like this:: $builder->addEventListener( FormEvents::PRE_SET_DATA, - function(FormEvent $event) { + function (FormEvent $event) { $form = $event->getForm(); // this would be your entity, i.e. SportMeetup @@ -556,7 +556,7 @@ The type would now look like:: )); ; - $formModifier = function(FormInterface $form, Sport $sport = null) { + $formModifier = function (FormInterface $form, Sport $sport = null) { $positions = null === $sport ? array() : $sport->getAvailablePositions(); $form->add('position', 'entity', array( @@ -568,7 +568,7 @@ The type would now look like:: $builder->addEventListener( FormEvents::PRE_SET_DATA, - function(FormEvent $event) use ($formModifier) { + function (FormEvent $event) use ($formModifier) { // this would be your entity, i.e. SportMeetup $data = $event->getData(); @@ -578,7 +578,7 @@ The type would now look like:: $builder->get('sport')->addEventListener( FormEvents::POST_SUBMIT, - function(FormEvent $event) use ($formModifier) { + function (FormEvent $event) use ($formModifier) { // It's important here to fetch $event->getForm()->getData(), as // $event->getData() will get you the client data (that is, the ID) $sport = $event->getForm()->getData(); @@ -730,7 +730,7 @@ all of this, use a listener:: public function buildForm(FormBuilderInterface $builder, array $options) { - $builder->addEventListener(FormEvents::POST_SUBMIT, function($event) { + $builder->addEventListener(FormEvents::POST_SUBMIT, function ($event) { $event->stopPropagation(); }, 900); // Always set a higher priority than ValidationListener diff --git a/cookbook/security/voters_data_permission.rst b/cookbook/security/voters_data_permission.rst index e27a86669be..b9e3afdb025 100644 --- a/cookbook/security/voters_data_permission.rst +++ b/cookbook/security/voters_data_permission.rst @@ -66,7 +66,6 @@ edit a particular object. Here's an example implementation: use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\User\UserInterface; - use Acme\DemoBundle\Entity\Post; class PostVoter implements VoterInterface { @@ -81,9 +80,11 @@ edit a particular object. Here's an example implementation: )); } - public function supportsClass($obj) + public function supportsClass($class) { - return $obj instanceof Post; + $supportedClass = 'Acme\DemoBundle\Entity\Post'; + + return $supportedClass === $class || is_subclass_of($class, $supportedClass); } /** @@ -92,7 +93,7 @@ edit a particular object. Here's an example implementation: public function vote(TokenInterface $token, $post, array $attributes) { // check if class of this object is supported by this voter - if (!$this->supportsClass($post)) { + if (!$this->supportsClass(get_class($post))) { return VoterInterface::ACCESS_ABSTAIN; }