I want to share a variable in all views but i'm not sure if this is the right way to do it? I have made a service provider:
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Worktype;
class ShareWorktypesInViewsProwider extends ServiceProvider
{
/**
* Bootstrap the application services.
*
* #return void
*/
public function boot()
{
$worktypes = Worktype::all();
view()->share('worktypes', $worktypes);
}
/**
* Register the application services.
*
* #return void
*/
public function register()
{
//
}
}
but i still get an error in my view. Do i need to register my service provider or it should work out of the box?
Firstly, for such a small piece of code I wouldn't worry about creating a brand new service provider. I would just add the above to your AppServiceProvider. Also, you code inline the above as well:
view()->share('worktypes', Worktype::all());
As for registering a provider. Literally all you have to do is go to config/app.php, find the providers array and add your provider to it.
In your can you would add:
App\Providers\ShareWorktypesInViewsProwider::class,
The documentation for it:
https://laravel.com/docs/5.3/providers#registering-providers
Hope this helps!
A more recent update on this. While #Rwd solution works great, you may run into difficulties as the service provider is run every single request.
As a result, you'll end up requesting Worktype from the database regardless of whether you're on a view, etc.
The best way to achieve this now is by using Laravel View composers.
By adding the below into your service provider, you'll only call the Worktype::all() when needed within a view.
view()->composer('*', function($view) {
$view->with(['worktypes' => Worktype::all()]);
});
Although make sure to use some caching otherwise it'll get called for every view!
Related
With Symfony 5.3.1 (and API Platform) I have implemented Security and generated a default User model. Now when someone posts an Article I would like the User object to be associated.
I tried in the Article model's constructor:
/* src/Entity/Article.php */
public function __construct()
{
$this->submittedBy = $this->getUser();
// Attempted to call an undefined method named \"getUser\" of class \"App\\Entity\\Article\
}
But it looks like $this->getUser() is only available to controllers.
Where is an appropriate place to set this as the default user value for new Articles please?
There are several ways you could do that. The simplest is when you construct the Article object, inject the user.
/* src/Entity/Article.php */
public function __construct($user)
{
$this->submittedBy = $user;
}
Then wherever you do new Article(); (likely in your controller) you need to change that to `new Article($this->getUser());
There are project that can do all this for you with listeners and so forth like the Blameable extension here
But until you understand the basics, I don't recommend moving on.
I would like to use the PUT method for creating resources. They are identified by an UUID, and since it is possible to create UUIDs on the client side, I would like to enable the following behaviour:
on PUT /api/myresource/4dc6efae-1edd-4f46-b2fe-f00c968fd881 if this resource exists, update it
on PUT /api/myresource/4dc6efae-1edd-4f46-b2fe-f00c968fd881 if this resource does not exist, create it
It's possible to achieve this by implementing an ItemDataProviderInterface / RestrictedDataProviderInterface.
However, my resource is actually a subresource, so let's say I want to create a new Book which references an existing Author.
My constructor looks like this:
/**
* Book constructor
*/
public function __construct(Author $author, string $uuid) {
$this->author = $author;
$this->id = $uuid;
}
But I don't know how to access the Author entity (provided in the request body) from my BookItemProvider.
Any ideas?
In API Platform many things that should occur on item creation is based on the kind of request it is. It would be complicated to change.
Here are 2 possibilities to make what you want.
First, you may consider to do a custom route and use your own logic. If you do it you will probably be happy to know that using the option _api_resource_class on your custom route will enable some listeners of APIPlaform and avoid you some work.
The second solution, if you need global behavior for example, is to override API Platform. Your main problem for this is the ReadListener of ApiPlatform that will throw an exception if it can't found your resource. This code may not work but here is the idea of how to override this behavior:
class CustomReadListener
{
private $decoratedListener;
public function __construct($decoratedListener)
{
$this->decoratedListener = $decoratedListener;
}
public function onKernelRequest(GetResponseEvent $event)
{
try {
$this->decoratedListener->onKernelRequest($event);
} catch (NotFoundHttpException $e) {
// Don't forget to throw the exception if the http method isn't PUT
// else you're gonna break the 404 errors
$request = $event->getRequest();
if (Request::METHOD_PUT !== $request->getMethod()) {
throw $e;
}
// 2 solutions here:
// 1st is doing nothing except add the id inside request data
// so the deserializer listener will be able to build your object
// 2nd is to build the object, here is a possible implementation
// The resource class is stored in this property
$resourceClass = $request->attributes->get('_api_resource_class');
// You may want to use a factory? Do your magic.
$request->attributes->set('data', new $resourceClass());
}
}
}
And you need to specify a configuration to declare your class as service decorator:
services:
CustomReadListener:
decorate: api_platform.listener.request.read
arguments:
- "#CustomReadListener.inner"
Hope it helps. :)
More information:
Information about event dispatcher and kernel events: http://symfony.com/doc/current/components/event_dispatcher.html
ApiPlatform custom operation: https://api-platform.com/docs/core/operations#creating-custom-operations-and-controllers
Symfony service decoration: https://symfony.com/doc/current/service_container/service_decoration.html
I want a variable bound to the application scope, (in java that would be application scope).
I thought service should be the thing to use in order to reach my goal.
I created a service test
<?php
namespace Acme\MyBundle\Service;
class test {
public $count;
public function __construct() {
$this->count = 0;
}
public function addCount() {
$this->count++;
}
}
which I declared in services.yml
acme.my.service.test:
class: Acme\MyBundle\Service\test
I call it in my controller
public function testAction() {
$this->get('acme.my.service.test')->addCount();
return $this->render('AcmeMyBundle:Test:test.html.twig');
}
But when I retrieve it in my twig, the value is 1, no matter how much I refresh or go with multiple session on the url bound to my testAction.
=> It means that constructor is called each time.
So is that the right way to do? I thought services were created once and reused then, but I may be mistaken.
Could you please enlighten me?
Thank you,
copndz
I found what I was looking for, APC caching system.
Easy to use and integrated to doctrine common.
How to cache in Symfony 2?
http://docs.doctrine-project.org/en/latest/reference/caching.html
I have setup a project for testing HTTP REST application using testNG / Maven / Springs RestTemplate.
I use it to do functional testing, multiple calls to the REST application are contained within suites to mimic user processes.
This is working fine.
Know we have turned on authentication.
Question is how to do this with testNG? How can i (only once) login for my test suite.
I can use a #BeforeSuite and call the loginpage, login and catch the cookie needed for all other requests. But where do i store this cookie so all test cases can add it?
I propably have to add some code to the tests to add the cookie of course....but how do i get hold of that?
I looked into #parameter and #dataprovider, but these seem not help me much...
Any help/suggestion is much appreciated.
I have created a workable solution.
What I have done is worked with a singleton object and with the #dataprovider, to get the data to the test:
The dataprovider creates a singleton object.
The singleton object calls the login page in its creation and will after every call from the different tests return the cookie information.
Maybe it is a bit of a hack, but it works.
The Singleton solution is somewhat heavy-handed as it prevents any parallelization of tests in the future.
There are some ways to solve this problem. One is to pass a ITestContext instance to your #BeforeSuite/#BeforeTest and #BeforeClass configuration methods and put/get the parameters via the test context in every instance:
public class Test {
/** Property Foo is set once per Suite */
protected String foo;
/** Property Foo is set once per Test */
protected String bar;
/**
* As this method is executed only once for all inheriting instances before the test suite starts this method puts
* any configuration/resources needed by test implementations into the test context.
*
* #param context test context for storing test conf data
*/
#BeforeSuite
public void beforeSuite(ITestContext context) {
context.setAttribute("foo", "I was set in #BeforeSuite");
}
/**
* As this method is executed only once for all inheriting instances before the test starts this method puts any
* configuration/resources needed by test implementations into the test context.
*
* #param context test context for storing test conf data
*/
#BeforeTest(alwaysRun = true)
public void beforeTest(ITestContext context) {
context.setAttribute("bar", "I was set in #BeforeTest");
}
/**
* This method is run before the first method of a test instance is started and gets all required configuration from
* the test context.
*
* #param context test context to retrieve conf data from.
*/
#BeforeClass
public void beforeClass(ITestContext context) {
foo = (String) context.getAttribute("foo");
bar = (String) context.getAttribute("bar");
}
}
This solution works even if the #BeforeSuite/Test/Class methods are in a superclass of the actual test implementation.
If you are delegating the login on Spring Security and your backend does not store state (means that only authorizes isolated requests) then you do not need to test it. This means that you can disable authentication (cookie obtaining) in your tests. This way you decouple the test itself from the authorization.
But if you do not want to do this. And If you organise your tests in suites you can set a private member. The cookie will be the header auth in the response.
#TestSuite
public void mySuite {
private String cookie;
#BeforeSuite public void login() {
// Obtain cookie
this.cookie = cookie;
}
////// Rest of suite
Another way to look at it is to execute login as a part of your test.
I do not know any other way more elegant of do it.
Does anyone have a good way to unit test an entity's validation constraints in Symfony2?
Ideally I want to have access to the Dependency Injection Container within the unit test which would then give me access to the validator service. Once I have the validator service I can run it manually:
$errors = $validator->validate($entity);
I could extend WebTestCase and then create a client to get to the container as per the docs however it doesn't feel right. The WebTestCase and client read in the docs as more of a facility to test actions as a whole and therefore it feels broken to use it to unit test an entity.
So, does anyone know how to either a) get the container or b) create the validator inside a unit test?
Ok since this got two votes I guess other people are interested.
I decided to get my shovel out and was pleasantly surprised (so far anyway) that this wasn't at all difficult to pull off.
I remembered that each Symfony2 component can be used in a stand alone mode and therefore that I could create the validator myself.
Looking at the docs at: https://github.com/symfony/Validator/blob/master/ValidatorFactory.php
I realised that since there was a ValidatorFactory it was trivial to create a validator (especially for validation done by annotations which I am, although if you look at the docblock on the page I linked above you'll also find ways to validate xml and yml).
First:
# Symfony >=2.1
use Symfony\Component\Validator\Validation;
# Symfony <2.1
use Symfony\Component\Validator\ValidatorFactory;
and then:
# Symfony >=2.1
$validator = Validation::createValidatorBuilder()->enableAnnotationMapping()->getValidator();
# Symfony <2.1
$validator = ValidatorFactory::buildDefault()->getValidator();
$errors = $validator->validate($entity);
$this->assertEquals(0, count($errors));
I hope this helps anyone else whose conscience wouldn't allow them to just use WebTestCase ;).
We end up rolling your own base test case to access the dependency container from within a test case. Here the class in question:
<?php
namespace Application\AcmeBundle\Tests;
// This assumes that this class file is located at:
// src/Application/AcmeBundle/Tests/ContainerAwareUnitTestCase.php
// with Symfony 2.0 Standard Edition layout. You may need to change it
// to fit your own file system mapping.
require_once __DIR__.'/../../../../app/AppKernel.php';
class ContainerAwareUnitTestCase extends \PHPUnit_Framework_TestCase
{
protected static $kernel;
protected static $container;
public static function setUpBeforeClass()
{
self::$kernel = new \AppKernel('dev', true);
self::$kernel->boot();
self::$container = self::$kernel->getContainer();
}
public function get($serviceId)
{
return self::$kernel->getContainer()->get($serviceId);
}
}
With this base class, you can now do this in your test methods to access the validator service:
$validator = $this->get('validator');
We decided to go with a static function instead of the class constructor but you could easily change the behavior to instantiate the kernel into the constructor directly instead of relying on the static method setUpBeforeClass provided by PHPUnit.
Also, keep in mind that each single test method in you test case won't be isolated fro, each others because the container is shared for the whole test case. Making modification to the container may have impact on you other test method but this should not be the case if you access only the validator service. However, this way, the test cases will run faster because you will not need to instantiate and boot a new kernel for each test methods.
For the sake of reference, we find inspiration for this class from this blog post. It is written in French but I prefer to give credit to whom it belongs :)
Regards,
Matt
I liked Kasheens answer, but it doesn't work for Symfony 2.3 anymore.
There are little changes:
use Symfony\Component\Validator\Validation;
and
$validator = Validation::createValidatorBuilder()->getValidator();
If you want to validate Annotations for instance, use enableAnnotationMapping() like below:
$validator = Validation::createValidatorBuilder()->enableAnnotationMapping()->getValidator();
the rest stays the same:
$errors = $validator->validate($entity);
$this->assertEquals(0, count($errors));
With Symfony 2.8, it seems that you can now use the AbstractConstraintValidatorTest class this way :
<?php
namespace AppBundle\Tests\Constraints;
use Symfony\Component\Validator\Tests\Constraints\AbstractConstraintValidatorTest;
use AppBundle\Constraints\MyConstraint;
use AppBundle\Constraints\MyConstraintValidator;
use AppBundle\Entity\MyEntity;
use Symfony\Component\Validator\Validation;
class MyConstraintValidatorTest extends AbstractConstraintValidatorTest
{
protected function getApiVersion()
{
return Validation::API_VERSION_2_5;
}
protected function createValidator()
{
return new MyConstraintValidator();
}
public function testIsValid()
{
$this->validator->validate(null, new MyEntity());
$this->assertNoViolation();
}
public function testNotValid()
{
$this->assertViolationRaised(new MyEntity(), MyConstraint::SOME_ERROR_NAME);
}
}
You have got a good sample with the IpValidatorTest class
The answer in https://stackoverflow.com/a/41884661/4560833 has to be changed a little for Symfony 4:
Use ConstraintValidatorTestCase instead of AbstractConstraintValidatorTest.
Answer (b): Create the Validator inside the Unit Test (Symfony 2.0)
If you built a Constraint and a ConstraintValidator you don't need any DI container at all.
Say for example you want to test the Type constraint from Symfony and it's TypeValidator. You can simply do the following:
use Symfony\Component\Validator\Constraints\TypeValidator;
use Symfony\Component\Validator\Constraints\Type;
class TypeValidatorTest extends \PHPUnit_Framework_TestCase
{
function testIsValid()
{
// The Validator class.
$v = new TypeValidator();
// Call the isValid() method directly and pass a
// configured Type Constraint object (options
// are passed in an associative array).
$this->assertTrue($v->isValid(5, new Type(array('type' => 'integer'))));
$this->assertFalse($v->isValid(5, new Type(array('type' => 'string'))));
}
}
With this you can check every validator you like with any constraint configuration. You neither need the ValidatorFactory nor the Symfony kernel.
Update: As #psylosss pointed out, this doesn't work in Symfony 2.5. Nor does it work in Symfony >= 2.1. The interface from ConstraintValidator got changed: isValid was renamed to validate and doesn't return a boolean anymore. Now you need an ExecutionContextInterface to initialize a ConstraintValidator which itself needs at least a GlobalExecutionContextInterface and a TranslatorInterface... So basically it's not possible anymore without way too much work.
I don't see a problem with the WebTestCase. If you don't want a client, don't create one ;) But using a possibly different service than your actual application will use, that's a potential pit fall. So personally, I've done like this:
class ProductServiceTest extends Symfony\Bundle\FrameworkBundle\Test\WebTestCase
{
/**
* Setup the kernel.
*
* #return null
*/
public function setUp()
{
$kernel = self::getKernelClass();
self::$kernel = new $kernel('dev', true);
self::$kernel->boot();
}
public function testFoo(){
$em = self::$kernel->getContainer()->get('doctrine.orm.entity_manager');
$v = self::$kernel->getContainer()->get('validator');
// ...
}
}
It's less DRY than Matt answer -- as you'll repeat the code (for each test class) and boot the kernel often (for each test method), but it's self-contained and require no extra dependencies, so it depends on your needs. Plus I got rid of the static require.
Also, you're sure to have the same services that your application is using -- not default or mock, as you boot the kernel in the environnement that you wish to test.
If people still read this one in 2023, prefer to inject the ValidatorInterface for Symfony > 3 / 4.
use Symfony\Component\Validator\Validator\ValidatorInterface;
// ...
$this->validator->validate($myEntity);