User current Locale inside entity OR trait - symfony

How can we access to current user local inside entity or Trait ?
namespace App\Entity;
class Activities
{
use Translation;
public $locale;
public function __construct(**PUT User locale**)
{
$this->locale = **PUT User locale**
}
}

You can't do that, because the locale is stored into the Request object which should'nt be accessed from an entity.
So if you really need that locale, you should pass it as an argument.
I suggest you to look around the Doctrine extension documentation for translating entities.
You'll see that it's better than just passing the user locale each time.
In fact, it is using event listener to pass the locale to the Translation component.

Related

Symfony 5.3.1 model field's default value to current User

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.

Hash user password without User instance in symfony

As it can be read in the official documentation, the current procedure to manually hash a password in the Symfony framework, is the following:
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
public function register(UserPasswordEncoderInterface $encoder)
{
// whatever *your* User object is
$user = new App\Entity\User();
$plainPassword = 'ryanpass';
$encoded = $encoder->encodePassword($user, $plainPassword);
$user->setPassword($encoded);
}
The encodePassword method requires an User instance to be passed as its first argument. The User instance must therefore pre-exist when the method is called: this means that a 'User' must be instantiated without a valid hashed password. I'd like the password to be provided as a constructor argument instead, so that an User entity is in a valid state when it is created.
Is there an alternative way of hashing the password using Symfony?
Update for Symfony 5 and later. The Encoder stuff was renamed to Hasher. The answer still works but just replace EncoderFactoryInterface with PasswordHasherFactoryInterface and change your variable names to hasher.
The UserPasswordEncoder uses what is known as an EncoderFactory to determine the exact password encoder for a given type of user. Adjust your code to:
public function register(EncoderFactoryInterface $encoderFactory)
{
$passwordEncoder = $encoderFactory->getEncoder(User::class);
$hashedPassword = $passwordEncoder->encodePassword($plainPassword,null);
And that should work as desired. Notice that getEncoder can take either a class instance or a class name.
Also note the need to explicitly send null for the salt. For some reason, some of the Symfony encoder classes do not have default values for salt yet.

Injecting an arbitrary parameter to a service

I'm wondering if there is a sort of best practice for the following case.
For instance, I have several services and inject them all as an array into a "factory" service. Then I call a method of this factory and want to get only one service depends on some conditions. After that I execute this service and get a result...
However, some of these services require a random string that I get from a client's request.
Of cause, I can call a service's method with this string as a parameter but several services do not require this string and I'll get "unused variable" in the method.
I guess that I could get service from the factory and then call a setter to add this string into the service. But it does not look like a stateless service.
Is there a more elegant solution to pass parameters that I could not inject into service nor use the setter for it?
Here how it looks in my code
First, I have an interface of all servers that I want to check. The service should support a customer an then it should render information from a DTO.
interface Renderable {
public function supports(Customer $customer);
public function render(CustomerDTO $dto);
}
Next, I have several services. This one uses DTO to render data.
class ServiceOne implements Renderable
{
public function suppots(Customer $customer)
{
return $customer->getPriority() === 1;
}
public function render(CustomerDTO $dto)
{
return 'One: '.$dto->getName();
}
}
However, some services do not need any DTO to render, they just provide a hardcoded value.
class ServiceTwo implements Renderable
{
public function suppots(Customer $customer)
{
return $customer->getPriority() !== 1;
}
// service does not use DTO, it simply output result
// so, I'll get a notice about unused variable
// and I can not remove it from the method since it is in interface
public function render(CustomerDTO $dto)
{
return 'Two';
}
}
This is a factory. It has all services injected as an array. Then it checks and returns the first service that supports a customer instance.
class ServiceFactory
{
/** #var Renderable[] */
private $services;
public function __construct(iterable $services)
{
$this->services = $services;
}
public function getRenderer(Customer $customer)
{
foreach ($this->services as $service)
{
if ($service->supports($customer)
{
return $service;
}
}
}
}
Here like I use factory and its result
$customer = ...; // it comes from a database
$request = ...; // it comes from a http request
$renderService = $factory->getRenderer($customer);
$customerDTO = CustomerDTO::createFromData([
'customerUid' => $customer->getUid(),
'date' => new \DateTime(),
'name' => $request->getSheetUid(),
'tags' => $request->getTags(),
]);
$renderService->render($customerDTO);
So, I have to call Renderer::render with a DTO instance. But some services do not use it to "render" data. I also can not inject it into a renderer service since this object (DTO) is built in a runtime when all services already injected. I also can not inject a RequestStack into service.
Since your parameter came from request - it can't be directly injected into service. Depending on actual logic of your services you can consider one of approaches listed below. Let's call your "random string that came from a client's request" a $requestParam for further reference.
In both cases you will need to get your $requestParam from actual Request object and pass it somewhere else. It can be done in a different ways, I would propose to create listener (e.g. RequestParamListener) for kernel.request event and put here a piece of code that takes parameter from Request and pass it further into this listener. Into approaches listed below I will assume that $requestParam will be passed in this way.
1. Separate provider
You can create separate class (e.g. RequestParamProvider) that will act as provider of this $requestParam for other services. It will receive $requestParam from RequestParamListener and other services that needs to get $requestParam will need to inject this provider and use its getRequestParam() method to obtain required parameter.
From my point of view it is the simplest approach and I would recommend it.
2. Direct injection by factory
Since you have some factory service - you can pass this $requestParam directly into factory and let it to initialize other services. Less flexible because you will need to implement initialization logic by itself and maintain it while project evolves.
3. Direct injection using interface
You can create separate interface (e.g. RequestParamAwareInterface) that will contain setRequestParam() method and let all classes that needs this $requestParam to implement this interface. After that you will need to write separate compiler pass that will collect all such classes (by iterating over ContainerBuilder and looking for implementation of particular interface by class inside service's definition) and pass array of these services to your RequestParamListener. Listener in its turn will be obligated to pass $requestParam for each of given services.
This approach will let your application to grow without need to sync $requestParam injection logic. However it will came at a cost of preliminary instantiation of all affected services regardless of actual further use of created instances.

Symfony / Doctrine - Access service inside an entity

I've create an Encryption service and want to access it inside an entity.
Stn like this:
public function setCompanyName(string $companyName, Encryption $encryption)
{
$this->companyName = $encryption->encrypt($companyName);
}
But is it possible to do it without calling this function setCompanyName with two parameters? Do I have to inject container and call service inside the function?
You want to store encrypted data in db right ?
Best way to do this is to create event listener that 'll fire up on each entity save , and make encryption there (you can inject anything you want to listener)
and second event listener that fire up when loading data from db to make decryption
If you do this right all encrytion/decryption thing will be transparent in code (it will only exists in those listeners )
look at this
https://symfony.com/doc/3.4/doctrine/event_listeners_subscribers.html
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/events.html
It is not possible to inject a service inside an entity.
But you could use an entity listener to use your service and encrypt the company name juste before persisting.
Take a look at https://symfony.com/doc/current/bundles/DoctrineBundle/entity-listeners.html
Maybe create a static method inside Encrypt class, and call it directly in entity?
class Encryption {
public static function encrypt(string $string) {
// your code
}
}
public function setCompanyName(string $companyName) {
$this->companyName = Encryption::encrypt($companyName);
}

Get the Repository of a Target Entity from the Abstract Class in Doctrine 2.5

Using Symfony 2.7 and Doctrine 2.5, I have
an Interface Alsciende\MyBundle\Model\CycleInterface
an abstract class Alsciende\MyBundle\Entity\Cycle that implements the interface
a final class AppBundle\Entity\Cycle that extends the abstract class and implements the interface
a doctrine orm configuration with resolve_target_entities that maps the interface to the final class
This system works well and I was able to create the database and implements some CRUD in AppBundle, manipulating the target entity directly.
However, I now want to manipulate the target entity in MyBundle, through the Interface. I need to get its repository:
$this->getDoctrine()->getRepository('Alsciende\MyBundle\Model\CycleInterface');
But I get the exception
class 'Alsciende\MyBundle\Model\CycleInterface' does not exist
How can I get the repository of the target entity? That is, how can I call ResolveTargetEntityListener directly to get the name of the entity implementing the interface?
edit:
Why do I need that? Very simply, for example, I need a controller that displays a list of all Cycles. The interface defines that each Cycle has an id and a name. I want to display every Cycle with its name and id. In order to do that, I need to access the repository of the actual Cycle entities.
Alsciende/MyBundle/Model/CycleInterface.php
<?php
namespace Alsciende\MyBundle\Model;
interface CycleInterface
{
public function getId();
public function getName();
}
Alsciende/MyBundle/Controller/CycleController.php
<?php
namespace Alsciende\MyBundle\Controller;
class CycleController extends Controller
{
public function indexAction()
{
$cycles = $this
->getDoctrine()
->getRepository('Alsciende\MyBundle\Model\CycleInterface')
->findAll();
// return template with list $cycles
// using only id and name properties
}
}
It's the same way that FosUserBundle is able to manage the User entities, even though the User class defined in FosUserBundle is an abstract class.
How can I get the repository of the target entity?
In app/config/config.yml put:
doctrine:
orm:
resolve_target_entities:
Namespace\InterfaceInterface: Namespace\Entity\TargetEntityImplementing
BUT
Why do I need that? Very simply, for example, I need a controller that displays a list of all Cycles. The interface defines that each Cycle has an id and a name. I want to display every Cycle with its name and id. In order to do that, I need to access the repository of the actual Cycle entities.
It's not a solution in this case, IMO. I would rather used entity with #DiscriminatorColumn configured:
http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/reference/inheritance-mapping.html#single-table-inheritance
Parent class will be some kind of interface you're looking for.
I recommend you to "merge" above: create a parent class which will implement such an interface, then map this interface to this class.

Resources