protected function execute(InputInterface $input, OutputInterface $output, UserPasswordHasherInterface $passwordHasher): int
{
$io = new SymfonyStyle($input, $output);
$arg_email = $input->getArgument('email');
$arg_password = $input->getArgument('password');
if ($arg_email || $arg_password) {
$io->note(sprintf('You used the email adress: %s', $arg_email));
$io->note(sprintf('You used the password: %s', $arg_password));
$user = New User();
dd($passwordHasher->hashPassword(
$user,
'test'
));
$user->setEmail($arg_email);
$user->setPassword($passwordHasher->hashPassword(
$user,
'test'
));
$user->setActive(1);
$em = $this->getDoctrine()->getEntityManager();
$em->persist($user);
$em->flush();
$io->success('User has been created!');
}
return Command::SUCCESS;
}
So I'm trying to build a custom command that will get your arg. (email/pass) and hash the password and create a user. Simple enough I thought.. I'm not quite sure whats going wrong because I'm getting weird compilere errors, I've tried multiple solutions provided by the internet but it comes all back to some what the same error.
"type" => 64 "message" => "Declaration of
App\Command\CreateUserCommand::execute(Symfony\Component\Console\Input\InputInterface
$input, Symfony\Component\Console\Output\OutputInterface $output,
App\Command\UserPasswordHasherInterface $passwordHasher): int must be
compatible with
Symfony\Component\Console\Command\Command::execute(Symfony\Component\Console\Input\InputInterface
$input, Symfony\Component\Console\Output\OutputInterface $output)"
"file" =>
"E:\BackYardBBQ\project\src\Command\CreateUserCommand.php"
Some of the things i tried are:
putting the interface inside the __constructor for auto wiring = getting an error that you cant use auto wiring
building it inside an controller or the authenticator = not working for the same reason my other failed attempts where.
building it inside the constructor of the controller
The error if you try autowiring
Cannot autowire service "App\Command\CreateUserCommand": argument
"$entityManager" of method "__construct()" has type
"App\Command\EntityManagerInterface" but this class was not found.
Make sure you enable namespaces
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use App\Entity\User;
You also need to add EntityManagerInterface and UserPasswordHasherInterface to your team constructor
private EntityManagerInterface $entityManager;
private UserPasswordHasherInterface $passwordHasher;
public function __construct(EntityManagerInterface $em, UserPasswordHasherInterface $passwordHasherInterface){
$this->entityManager = $em;
$this->passwordHasher = $passwordHasherInterface;
parent::__construct();
}
Now you will be able to use them in your command like this:
$user->setPassword($this->passwordHasher->hashPassword($user, 'test'));
$this->entityManager->persist($user);
$this->entityManager->flush();
Related
I'm trying to save an encoded password to my entity by using the UserPasswordEncoderInterface class.
class UserFixture extends Fixture
{
private $encoder;
public function _construct( UserPasswordEncoderInterface $encoder){
$this->encoder = $encoder;
}
public function load(ObjectManager $manager)
{
$user = new User();
$user->setUsername('admin');
$user->setPassword(
$this->encoder->encodePassword($user, '0000')
);
$user->setEmail('no-reply#hotmail.com');
$manager->persist($user);
$manager->flush();
}
}
But I'm getting the follwing error: Call to a member function encodePassword() on null. Can't find what I'm doing wrong here!
You should first check your namespace, but as #Cerad says, you mistyped __construct() which is the reason why your encoder is null.
The right command to use in order to load your fixture properly might be :
php bin/console doctrine:fixtures:load
As the documentation recommends : https://symfony.com/doc/master/bundles/DoctrineFixturesBundle/index.html
First I will explain why and how the solution works and then the problems I have encountered. If you think there is a better way to do what I do, I'd love to hear it. I would also like to know why doctrine behaves in this way.
It turns out that my aplication needs to connect to a different database according to the client. I have a table, in a fixed database, containing the connection information that is used in some request.
I have had success with the following code:
class DynamicEntityManager {
protected $em;
private $request;
private $client_id;
public function __construct(RequestStack $request, EntityManagerInterface $em){
$this->em = $em;
$this->request = $request;
}
public function getEntityManager(ClientConn $client = null) {
$request = $this->request->getCurrentRequest();
if($client == NULL){
$domain = $request->attributes->get('domain');
if($domain == "" || $domain == NULL){
throw new \Exception("Error de conexion", 1);
}
$client = $this->em->getRepository(ClientConn::class)->findOneBy(array(
"subdomain" => $domain
));
if($client == NULL){
throw new \Exception("Error de conexion", 1);
}
}
$connectionDB = $client->getConnection();
$dbdriver = 'oci8';
$conexionSplit = explode(':',$connectionDB);
$dbhost = $conexionSplit[0];
$dbport = $conexionSplit[1];
$dbname = $conexionSplit[2];
$dbuser = $client->getUsuarioBd();
$dbpass = $client->getClaveBd();
$service = false;
$this->client_id = $client->getId();
if(strpos($dbname,'SN=') !== false){
$parts = explode('=',$dbname);
$dbname = $parts[1];
$service = true;
}
$request->attributes->set('client_id',$client->getId());
$conn = array(
'driver' => $dbdriver,
'host' => $dbhost,
'port' => $dbport,
'dbname' => $dbname,
'user' => $dbuser,
'password' => $dbpass,
'service' => $service,
'charset' => 'UTF8',
'schema' => null
);
return EntityManager::create($conn, $this->em->getConfiguration());
}
}
As you can see I return EntityManager::create($conn, $this->em->getConfiguration ()) with the new connection. The way I use it is the next:
/**
* #Route("/api/client/{id}/conf/{confID}", name="conf.show")
* #Method({"GET"})
*/
public function show(ClientConn $client, Request $request, DynamicEntityManager $dem ,$confId){
try {
$em = $dem->getEntityManager($client);
$entity = $em->getRepository(Configuration::class)->find($confId);
return new JsonResponse($entity, 200);
}
catch(\Exception $ex) {
return new JsonResponse([
"excepcion" => $ex->getMessage()
], $ex->getCode());
}
}
It works as expected or so I believed until I saw that when the entity has a custom repository it is unable to use the dynamic connection and therefore the previous route will return a table not found exception.
#ORM\Entity() <-- Works like a charm
#ORM\Entity(repositoryClass="App\Repository\ConfigurationRepository")<-- Table not found.
It works in the repository if I create the connection again, although I do not like the solution. So, what do I want? I would like to be able to use the basic methods like find (), findBy () and others without having to rewrite them every time I use a custom repository.
class ConfigurationRepository extends ServiceEntityRepository
{
public function __construct(RegistryInterface $registry, DynamicEntityManager $dem)
{
parent::__construct($registry, Configuration::class);
$this->dem= $dem;
}
public function uglyFind($client, $confID)
{
$query = $this->dem->getEntityManager($client)->createQueryBuilder('conf')
->select("conf")
->from(ConfPedidosLentes::class,'conf')
->where('conf.id = :value')->setParameter('value', $confID)
->getQuery();
return $query->getOneOrNullResult();
}
I will really appreciate any contribution and thought in this matter.
Instead of:
class ConfigurationRepository extends ServiceEntityRepository
{
public function __construct(RegistryInterface $registry, DynamicEntityManager $dem)
{
parent::__construct($registry, Configuration::class);
$this->dem= $dem;
}
...
try extending EntityRepository (without using a constructor) and use find as you did in your controller:
use Doctrine\ORM\EntityRepository;
class ConfigurationRepository extends EntityRepository
{
}
ServiceEntityRepository is an optional EntityRepository base class with a simplified constructor for autowiring, that explicitly sets the entity manager to the EntityRepository base class. Since you have not configured your doctrine managers to handle these connections properly (it's not even possible actually with so many connections), ServiceEntityRepository will pass a wrong EntityManager instance to the EntityRepository subclass, that's why you should not extend ServiceEntityRepository but EntityRepository.
I have a controller that renders a form that is suppose to have a dropdown with titles mapped against a client_user entity. Below is code I use in my controller to create the form:
$builder = $this->get(form.factory);
$em = $this->get('doctrine.entity_manager');
$form = $builder->createBuilder(new ClientUserType($em), new ClientUser())->getForm();
Below is my ClientUserType class with a constructor that I pass the entity manager on:
<?php
namespace Application\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
class ClientUserType extends AbstractType
{
protected $entityManager;
public function __construct($entityManager)
{
$this->entityManager = $entityManager;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title', EntityType::class, array(
'class' => '\\Application\\Model\\Entity\\Title',
'em' => $this->entityManager
))
->add('name')
->add('surname')
->add('contact')
->add('email');
}
public function getName()
{
return 'client_user_form';
}
}
I keep on getting this catchable fatal error below and have no idea what I need to do in order to get a dropdown with titles from a database with doctrine.
Catchable fatal error: Argument 1 passed to Symfony\Bridge\Doctrine\Form\Type\DoctrineType::__construct() must be an instance of Doctrine\Common\Persistence\ManagerRegistry, none given, called in D:\web\playground-solutions\vendor\symfony\form\FormRegistry.php on line 90 and defined in D:\web\playground-solutions\vendor\symfony\doctrine-bridge\Form\Type\DoctrineType.php on line 111
Reading from that error I have no idea where I need to create a new instance of ManagerRegistry registry as it appears that the entity manager does not work. I am also thinking perhaps I need to get the ManagerRegistry straight from the entity manager itself.
Can someone please help explain the simplest way to get this to work? What could I be missing?
Seems that doctrine-bridge form component is not configured.
Add class
namespace Your\Namespace;
use Doctrine\Common\Persistence\AbstractManagerRegistry;
use Silex\Application;
class ManagerRegistry extends AbstractManagerRegistry
{
protected $container;
protected function getService($name)
{
return $this->container[$name];
}
protected function resetService($name)
{
unset($this->container[$name]);
}
public function getAliasNamespace($alias)
{
throw new \BadMethodCallException('Namespace aliases not supported.');
}
public function setContainer(Application $container)
{
$this->container = $container;
}
}
and configure doctrine-bridge form component
$application->register(new Silex\Provider\FormServiceProvider(), []);
$application->extend('form.extensions', function($extensions, $application) {
if (isset($application['form.doctrine.bridge.included'])) return $extensions;
$application['form.doctrine.bridge.included'] = 1;
$mr = new Your\Namespace\ManagerRegistry(
null, array(), array('em'), null, null, '\\Doctrine\\ORM\\Proxy\\Proxy'
);
$mr->setContainer($application);
$extensions[] = new \Symfony\Bridge\Doctrine\Form\DoctrineOrmExtension($mr);
return $extensions;
});
array('em') - em is key for entity manager in $application
For others that may find this: If you want to use the EntityType and you're not using a framework at all, you need to add the DoctrineOrmExtension to your FormFactoryBuilder like so:
$managerRegistry = new myManagerRegistry(
'myManager',
array('connection'),
array('em'),
'connection',
'em',
\Doctrine\ORM\Proxy\Proxy::class
);
// Setup your Manager Registry or whatever...
$doctrineOrmExtension = new DoctrineOrmExtension($managerRegistry);
$builder->addExtension($doctrineOrmExtension);
When you use EntityType, myManagerRegistry#getService($name) will be called. $name is the name of the service it needs ('em' or 'connection') and it needs to return the Doctrine entity manager or the Doctrine database connection, respectively.
In your controller, try to call the service like that:
$em = $this->get('doctrine.orm.entity_manager');
Hope it will help you.
Edit:
Sorry, I thought you was on Symfony... I have too quickly read...
I'm running the equivalent of this code in lots and lots of controller actions, basically it grabs the user's username, and if that username is attached to a blog entity it will allow the user to see the blog entity(s):
$em = $this->getDoctrine()->getManager();
$user = $this->get('security.context')->getToken()->getUser();
$entities = $em->getRepository('MySiteBundle:Blog')->findBy(array('user' => $user));
return $this->render('MySiteBundle:Blog:index.html.twig', array(
'entities' => $entities,
I want to move it into a service so I can cut down on code repetition. I want to avoid doing as much logic in my controllers as possible.
That being said, I'm not sure how I can access the user session and doctrine in a service.
Here's my services.yml:
mysite.user.blog:
class: MySite\SiteBundle\Services\BlogUser
And here's how I was attempting to call it in the controller:
public function testAction() {
$response = $this->get('mysite.user.blog');
return new Response($response);
}
I did try using an event subscriber/listener tag, but that doesn't seem to accomplish the task I want.
Here is my completely horrible attempt at a service. I couldn't get any response from it without using a constructor.
namespace MySite\SiteBundle\Services;
use MySite\SiteBundle\Entity\Blog;
class BlogUser {
protected $entities;
public function __construct(){
$em = $this->getDoctrine()->getManager();
$user = $this->get('security.context')->getToken()->getUser();
$this->entities = $em->getRepository('MySiteBundle:Blog')->findBy(array('user' => $user));
}
}
Am I going about this the completely wrong way? Is there a better way that I'm missing?
EDIT/ANSWER:
modified my naming convention a little:
//services.yml
mysite.user.blog.entities:
class: Mysite\SiteBundle\Services\BlogUser
arguments: ["#doctrine.orm.entity_manager", "#security.context"]
In the controller action:
$userEntities = $this->get('mysite.user.blog.entities');
$entities = $userEntities->getEntities();
In the service itself:
class BlogUser {
protected $entities;
public function __construct($em, $securityContext){
$user = $securityContext->getToken()->getUser();
$this->entities = $em->getRepository('MySiteBundle:Blog')->findBy(array('user' => $user));
}
public function getEntities(){
return $this->entities;
}
}
Still needs two lines to get the $entities variable in the controller, but this is way better than defining the same thing over and over.
"Security.context" has been deprecated since Symfony 2.6
After some community discussions, it was decided that SecurityContext gives too many dependencies to retrieve a simple Token/User object. That's why, starting with Symfony 2.6, thesecurity.context service has been deprecated and split into two new services:security.authorization_checker and security.token_storage.
Source
Thus, the new way to do it would be, first configure your service as:
mysite.user.blog:
class: MySite\SiteBundle\Services\BlogUser
arguments: ["#doctrine.orm.entity_manager", "#security.token_storage"]
Then in the service class constructor:
class BlogUser
{
protected $user;
protected $entities;
public function __construct(EntityManager $em, TokenStorage $tokenStorage)
{
$this->user = $tokenStorage->getToken()->getUser();
$this->entities = $em->getRepository('MySiteBundle:Blog')->findBy(array('user' => $user));
}
}
Yes, you are doing it in wrong way. Let's look at your code:
# call to undefined object method getDoctrine()
$em = $this->getDoctrine()->getManager();
# call to undefined object method get()
$user = $this->get('security.context')->getToken()->getUser();
You cannot call getting entitymanager and security.context in your service in the same way like in your controller. Instead, you have to inject entitymanager and security.context services. Example:
# services.yml
mysite.user.blog:
class: MySite\SiteBundle\Services\BlogUser
calls:
- [ setUserFromSecurityContext, [ #security.context ]]
- [ setEntityManager, [ #doctrine.orm.entity_manager ]]
And improved service:
namespace Catablog\SiteBundle\Services;
use MySite\SiteBundle\Entity\Blog;
class BlogUser {
private $entityManager;
private $user;
public function setEntityManager(EntityManager $entityManager)
{
$this->entityManager = $entityManager;
}
public function setUserFromSecurityContext(SecurityContext $securityContext)
{
# notice, there are a cases when `getToken()` returns null, so improve this
$this->user = $securityContext->getToken()->getUser();
}
public function getEntities(){
# your code here
}
}
More info about Dependency injection
You are looking on how to 'inject' other services into your custom service. Take a look at Service Container documentation.
In your case, you can inject doctrine.orm.entity_manager and security.context services into your BlogUser class via constructor injection. For example:
class BlogUser {
public function __construct($em, $securityContext) {
$user = $securityContext->getToken()->getUser();
$this->entities = $em->getRepository('MySiteBundle:Blog')->findBy(array('user' => $user));
}
}
And configure your service as the following:
mysite.user.blog:
class: MySite\SiteBundle\Services\BlogUser
arguments: ["#doctrine.orm.entity_manager", "#security.context"]
Could any of you symfony2 gurus enlighten me as to how I can write a flash message from a symfony2 service?
I thought I had what I needed when I injected the container as below, but apparently not, I get error
Fatal error: Call to undefined method appDevDebugProjectContainer::getRequest() in /var/www/cloudsign_beta/src/BizTV/CommonBundle/Helper/globalHelper.php on line 135
So apparently I can not access the request... If I have to pass that as well from the controller I will soon loose the point of a service, it being unable to do anything by itself =)
<?php
namespace BizTV\CommonBundle\Helper;
use Symfony\Component\DependencyInjection\ContainerInterface as Container;
use Doctrine\ORM\EntityManager as EntityManager;
use BizTV\CommonBundle\Entity\Log;
class globalHelper {
private $container;
private $em;
public function __construct(Container $container, EntityManager $em) {
$this->container = $container;
$this->em = $em;
}
public function log($type,$message) {
// currently $type can be 'success', 'fail' or 'error'.
$currentUser = $this->container->get('security.context')->getToken()->getUser();
$currentCompany = $this->container->get('security.context')->getToken()->getUser()->getCompany();
//if the $type is one that we want to write to the log then create log entity (we don't log failed attempts at operations, but we do log errors.
if ($type == 'success') {
$em = $this->em;
$now = new \DateTime("now");
$entity = new Log();
$entity->setCompany($currentCompany);
$entity->setExecutor($currentUser);
$entity->setTime($now);
$entity->setEventType($type);
$entity->setEventMessage($message);
$em->persist($entity);
$em->flush();
}
//flash out the $message message text
$container = $this->container;
$session = $container->getRequest()->getSession()->setFlash($type, $message);
}
}
$session = $container->get('request')->getSession()->setFlash($type, $message);
But be careful as you may not be aware of whether request exists or not. A proper way of managing this would be by restricting your service to the request scope.