Hello i have problem with my test.
I'm trying to test UserFactory which is creating UserObject by UserDto data.
I dont know how to test it because factory need PasswordEncoder in dependencies.
use App\Entity\User;
use App\Service\Factory\UserFactory;
use PHPUnit\Framework\MockObject\MockObject as MockObject;
use PHPUnit\Framework\TestCase;
class UserFactoryTest extends TestCase
{
/**
* #covers UserFactory
*/
public function testShouldCreateUserObjectFromUserDto()
{
//Given
/**
* #var UserDto | MockObject
*/
$userDto = $this->getMockBuilder(UserDto::class);
//When
$userFactory = new UserFactory(/* PASSWORD ENCODER */);
$user = $userFactory->create($userDto);
//Then
$this->assertInstanceOf(User::class, $user);
}
}
namespace App\Service\Factory;
use App\Entity\User;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
class UserFactory
{
/**
* #var UserPasswordEncoderInterface
*/
private $encoder;
public function __construct(UserPasswordEncoderInterface $encoder)
{
$this->encoder = $encoder;
}
public function create(UserDto $dto)
{
$user = new User();
$user->setPassword($this->encoder->encodePassword($dto->plainPassword));
/**
* CODE..
*/
return $user;
}
}
Is this right? how can i test this factory which have dependencies?
i cant user __construct in my TestClass
(Hint: apparently you're using the UserPasswordEncoderInterface wrong, because it's not a PasswordEncoderInterface - the former additionally expects the User as a param, while the latter does not, which I just learned today, you might want to fix that)
In general, you have to provide all dependencies for the class to be tested. There are different approaches, essentially: Implementing a reduced version of the interface (if it's an interface), actually finding the dependency and instantiate it or use a mock and tell it what will happen to it. The latter can be done if you generally know what happens with the dependency (non-blackbox testing).
So, you could just mock the interface with some well defined behavior:
//$upe = $this->getMockBuilder(\Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface::class)->getMock();
$upe = $this->getMockBuilder(\Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface::class)->getMock();
$upe->expects($this->any())
->method('encodePassword')
// omit following line, if you don't want to check for param
->with($this->equalTo('plainPassword'))
->willReturn('encodedPassword');
and then provide that mock to your userfactory:
$userfactory = new UserFactory($upe);
this is somewhat dependant on the implementation of UserFactory though, expecting it to essentially be called in the specified way and which will return the same string always. (You can go deeper into mocking and/or implement the interface yourself to have more control)
is it possible to create a relation to a generic table/class whith Doctrine?
Here is some code to make it easier to understand:
// class Log...
// TODO:
// It could be useful to have a reference to
// the element mentioned by the log, the issue is
// we don't know what kind of entity it is.
/**
* #ORM\ManyToOne(targetEntity="???")
*/
private $elementId
Maybe instead of using targetEntity I could just use an int that is the id of the element located in the unknow table.
There is no built-in possibility now.
Let me propose a work around using Doctrine Lifecycle Events :
Create 3 properties :
/*
* #ORM\Column(name="element_class", type="string")
*/
private $elementClass
/*
* #ORM\Column(name="element_id", type="integer")
*/
private $elementId
// Not mapped
private $element
public function setElement($element)
{
$this->element = $element;
$this->elementClass = get_class($element);
$this->elementId = $element->getId();
}
public function getElement()
{
return $this->element;
}
// You need these for the PostLoad event listener :
public function hydrateElementPostLoad($element)
{
$this->element = $element;
}
public function getElementClass()
{
return $this->elementClass;
}
public function getElementId()
{
return $this->elementId;
}
Then create a PostLoadListener able to hydrate the element property :
namespace AppBundle\EventListener;
use Doctrine\ORM\Event\LifecycleEventArgs;
use AppBundle\Entity\Log;
class PostLoadListener
{
public function postLoad(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
if($entity instanceOf Log){
$em = $args->getEntityManager();
$entity->hydrateElementPostLoad(
$this->em->getRepository($entity->getElementClass())->findOneById($entity->getElementId())
);
}
}
}
And register this event in your services.yml :
services:
places.listener:
class: AppBundle\EventListener\PostLoadListener
tags:
- { name: doctrine.event_listener, event: postLoad }
That's also how the most famous Bundle for logging works (The Gedmo DoctrineExtensions Logger)
To retrieve all logs for an entity, create a repository method for your Log entity :
getLogs($entity)
{
return $this->_em->findBy(array(
'element_id'=>$entity->getId(),
'element_class'=>get_class($entity)
));
}
You are trying to manage some abstraction of one or more of your entities in the database level which is a headache,
Doctrine already has proposed Somme solutions to manage this kind of abstractions by using Inheritance Mapping
A mapped superclass is an abstract or concrete class that provides persistent entity state and mapping information for its subclasses, but which is not itself an entity. Typically, the purpose of such a mapped superclass is to define state and mapping information that is common to multiple entity classes.
For more information check this
using symfony 2.8, i'm working with subdomains and i want to show different (lets say)home pages depending on the subdomain, i'm storing the subdomains in Domain table with a column named subdomain. ideally when the user visits sub.example.com i want to search the database for 'sub' and get the id of that row and set that as a global parameter for that specific domain, so that i can load the websitesettings and load other dynamic data from the database (using domain_id as the key)
this is what i presume to be correct, if there are better methods to deal with this same problem, please let me know, i might get a friend to give out a bounty if its new to me.
I suggest you listen to the kernel.controller event. Make sure your listener is container aware so that you can set the parameter by doing $this->container->setParameter('subdomain', $subdomain);
At this point you just need to check the parameter you set where it suits you, for example in your controller action so that you can return, for example, different views according to the current subdomain.
Reference:
Container aware dispatcher
Symfony2 framework events
Have a look at my implementation, using a YAML configuration instead of a database: https://github.com/fourlabsldn/HostsBundle. You might be able to get some inspiration.
<?php
namespace FourLabs\HostsBundle\Service;
use Symfony\Component\HttpFoundation\RequestStack;
use FourLabs\HostsBundle\Model\DomainRepository;
use FourLabs\HostsBundle\Exception\NotConfiguredException;
abstract class AbstractProvider
{
/**
* #var RequestStack
*/
protected $requestStack;
/**
* #var DomainRepository
*/
protected $domainRepository;
/**
* #var boolean
*/
protected $requestActive;
public function __construct(RequestStack $requestStack, DomainRepository $domainRepository, $requestActive)
{
$this->requestStack = $requestStack;
$this->domainRepository = $domainRepository;
$this->requestActive = $requestActive;
}
protected function getDomainConfig()
{
$request = $this->requestStack->getCurrentRequest();
if(is_null($request) || !$this->requestActive) {
return;
}
$host = parse_url($request->getUri())['host'];
if(!($domain = $this->domainRepository->findByHost($host))) {
throw new NotConfiguredException('Domain configuration for '.$host.' missing');
}
return $domain;
}
}
and the listener
<?php
namespace FourLabs\HostsBundle\EventListener;
use FourLabs\HostsBundle\Service\LocaleProvider;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\HttpKernelInterface;
class LocaleListener
{
/**
* #var LocaleProvider
*/
private $localeProvider;
public function __construct(LocaleProvider $localeProvider) {
$this->localeProvider = $localeProvider;
}
public function onKernelRequest(GetResponseEvent $event) {
if(HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
return;
}
$event->getRequest()->setLocale($this->localeProvider->getLocale());
}
}
I am trying to resolve class via __construct using Laravel's bind() method.
Here what I do:
routes.php (of course I will move it away from here)
// Bindings
App::bind(
'License\Services\ModuleSelector\SelectorInterface',
'License\Services\ModuleSelector\ModuleSelector'
);
SelectorInterface.php - interface that I will expect in __construct method.
<?php namespace License\Services\ModuleSelector;
interface SelectorInterface {
/**
* Simply return query that will select needle module fields
*
* #return mixed
*/
public function make();
}
ModuleSelector.php - this is class that I want to resolve via Laravel's DI (see example below).
<?php namespace License\Services\ModuleSelector;
use License\Services\ModuleSelector\Selector;
class ModuleSelector extends Selector
{
/**
* Get module by it's code
*
* #return mixed
*/
public function find()
{
return $this->make()
->where('code', $module_code)
->first();
}
}
Module.php
<?php namespace License\Services\ModuleType;
use License\Services\ModuleType\TypeInterface;
use License\Services\ModuleSelector\SelectorInterface;
class Module
{
...
function __construct(SelectorInterface $selector)
{
$this->selector = $selector;
}
...
}
And the place when error occurs:
In my repo I have use License\Services\ModuleType\Module as ModuleService;.
Than there is method called find():
/**
* Find module by its code with all data (types, selected type)
* #return mixed
*/
public function find($module_code)
{
$module = new ModuleService;
// Get module id in order to use build in relations in framework
$module = $this->module->find($module_code);
...
}
So, in other words, I have 2 classes and one interface. What I am trying to do is:
1) Create Class1.php / Class2.php / Class2Interface.php.
2) In Class1.php in the __construct I specify __construct(Class2Interface $class2).
3) Instantiate Class2.
What I am doing wrong? Examples found here.
In this line:
$module = new ModuleService;
You are directly invoking the Module class and not passing in an instance of SelectorInterface.
For the IoC to work you bind and make classes using it. Try that line again with :
$module = App::make('License\Services\ModuleSelector\SelectorInterface');
An alernative is to inject it directly into your repos constructor, as long as the repo is created by the IoC container, your concrete will be automatically injected.
Nowhere do you have a class marked to actually "implement SelectorInterface".
I'm programming in PHP procedurally (is this even a word?) for about five years now and decided to try an OOP approach but ran into some concept/design problems. Let's say you have some modules in the program, every module has the possibility to list, add, edit and delete an entity. An entity can be..dunno, user, client, product etc.
How would you design the classes to manipulate these entityes?
Two possibilities came in my mind:
create classes for every entity with methods like getUsersList, addUser, editUser, delUser
This approach seems resource consumingbecause in the script for the listing you only need the getUsersList and maybe delUser methods, while in the add user popup script, you only need the addUser method and in the edit user popup script only the editUser method. So, you have to instanciate an object and only use two or one of it's methods...
create general classes: listing, add, edit and delete and extend them for every entity this way you only have to instanciate one class at a time (the one you really need)
Thanks in advance,
I would create an interface defining your list, add, edit, and delete methods. This gives you a class "template". If your classes (User, Client, Product, etc.) implement this interface, then the methods in the interface must be defined in those classes.
This will give you a similar "API" to access all the functionality of every class that implements your interface. Since each of your listed objects contains different data, the details of the methods will be different, and thus separate, but the interface will be the same.
Aside:
Your inclusion of "list" in your list of methods concerns me a little. It seems to imply that you are seeing your objects as collections of Users, Clients, Products, etc, where there should most likely be a User class that represents a single user, a Client class that represents a single client, etc.
On the other hand, "list" may be handled as a static method - a method that can be called without an instance of the class.
$bob = new User('bob');
$bob->add(); // Adds bob to the database
$fred = new User('fred');
$fred->add(); // Adds fred to the database
$users = User::list(); // Gives an array of all the users in the database
That's how I would handle things, anyway.
You will need to create a solid architecture and framework for managing your data model. This is not easy and will only get more complex as the data model grows. I would highly recommend using a PHP framework (Symfony, CakePHP, etc), or at least, an ORM (Doctrine, Propel, etc).
If you still want to roll your own, I would start with an architecture similar to below.
You will want a DbRecord class that is used for individual record operations (like saving, deleting, etc). This DbRecord class will be extended by specific entities and will provide the foundation for basic entity operations.
class DbRecord {
public function save() {
// save logic (create or update)
}
public function delete() {
// delete logic
}
// other record methods
}
class User extends DbRecord {
private $name;
private $email;
public function setName($name_) {
$this->name = $name_;
}
public function setEmail($email_) {
$this->email = $email_;
}
}
From which, you can perform individual record operations:
$user = new User();
$user->setName('jim');
$user->setEmail('jim#domain.com');
$user->save();
You will now want a DbTable class that is used for bulk operations on the table (like reading all entities, etc).
class DbTable {
public function readAll() {
// read all
}
public function deleteAll() {
// delete all logic
}
public function fetch($sql) {
// fetch logic
}
// other table methods
}
class UserTable extends DbTable {
public function validateAllUsers() {
// validation logic
}
// ...
}
From which, you can perform bulk/table operations:
$userTable = new UserTable();
$users = $userTable->readAll();
foreach ($users as $user) {
// etc
}
Code architecture is the key to a website scaling properly. It is important to divide the data model into the appropriate classes and hierarchy.
Again, as your website grows, it can get very complicated to manage the data model manually. It is then when you will really see the benefit of a PHP framework or ORM.
NOTE: DbRecord and DbTable are arbitrary names - use w/e name you like.
Use your first method, where you create a reusable object with methods. It is not a waste of time as you only code it once.
class User {
function __construct() { /* Constructor code */ }
function load($id) { ... }
function save() { ... }
function delete() { ... }
}
You're on the right track with 'general classes' (also called base classes, or abstract classes in case their behaviour NEEDS to be complemented by child classes before they can be put to use).
The OOP approach would be to put all behavior that is common to all entities in the base classes.
If you use something akin to ActiveRecord, you already have a general (abstract) interface for create-update-delete operations. Use that to your advantage, and let your base classes operate ONLY on those interface methods. They don't need to know they are updating a Product, or a a User, they just need to know they can call the update() method on an entity.
But even without using something quite feature-heavy like an AR framework (check out Doctrine if you're interested in a very flexible ORM..) you can use interfaces to abstract behavior.
Let me give you a more elaborate example...
/**
* Interface for all entities to use
*/
interface Entity {
static function newEntity();
static function fetch($id);
function save();
function setProperties(array $properties);
function delete();
}
/**
* A concrete product entity which implements the interface
*/
class Product implements Entity {
public $productId;
public $name;
public $price;
public $description;
/**
* Factory method to create a new Product
*
* #param integer $id Optional, if you have auto-increment keys you don't need to set it
* #return Product
*/
public static function newEntity($id=NULL) {
$product = new Product();
$product->productId = $id;
return $product;
}
/**
* Factory method to fetch an existing entity from the database
*
* #param integer $id
* #return Product
*/
public static function fetch($id) {
// make select with supplied id
// let $row be resultset
if (!$row) {
return NULL; // you might devise different strategies for handling not-found cases; in this case you need to check if fetch returned NULL
}
$product = new Product();
$product->productId = $id;
$product->name = $row['name'];
$product->price = $row['price'];
$product->description = $row['description'];
return $product;
}
/**
* Update properties from a propreties array
* #param array $properties
* #return void
*/
public function setProperties(array $properties) {
$this->name = $properties['name'];
$this->price = $properties['price'];
$this->description = $properties['description'];
}
public function save() {
// save current product properties to database
}
public function delete() {
// delete product with $this->productId from database
}
}
/**
* An abstract CRUD controller for entities
*/
abstract class EntityCrudController {
protected $entityClass = 'UNDEFINED'; // Override this property in child controllers to define the entity class name
protected $editTemplate = NULL; // Override this to set an edit template for the specific entity
protected $templateEngine; // Pseudo-Templating engine for this example
/**
* Display the edit form for this entity
* #param integer $entityId
* #return string
*/
public function editAction($entityId) {
// Fetch entity - this is not the most clean way to fetch, you should probably consider building a factory that encapsulates this.
$entity = call_user_func($this->entityClass, 'fetch', $entityId);
// Assign entity to your edit template, in this example I'm assuming we're using a template engine similar to Smarty
// You can generate the HTML output in any other way you might like to use.
$this->templateEngine->setTemplate($this->editTemplate);
$this->templateEngine->assign('entity', $entity);
return $this->template->render();
}
/**
* Update an existing entity
*
* #param integer $entityId
* #param array $postArray
* #return string
*/
public function updateAction($entityId, array $formArray) {
// Be sure to validate form data first here, if there are errors call $this->editAction() instead and be sure to set some error information
$entity = call_user_func($this->entityClass, 'fetch', $entityId);
$entity->setProperties($formArray);
$entity->save();
// Again, using our imaginary templating engine to display...
$this->templateEngine->setTemplate($this->editTemplate);
$this->templateEngine->assign('entity', $entity);
$this->templateEngine->assign('message', 'Saved successfully!');
return $this->template->render();
}
// Devise similar generic methods for newAction/insertAction here
}
/**
* Concrete controller class for products
* This controller doesn't do much more than extend the abstract controller and override the 2 relevant properties.
*/
class ProductCrudController extends EntityCrudController {
protected $entityClass = 'Product';
protected $editTemplate = 'editProduct.tpl';
}
// Usage example:
// Display edit form:
$controller = new ProductCrudController();
$htmlOutput = $controller->editAction(1);
// Save product:
$htmlOutput = $controller->updateAction(1, array('name' => 'Test Product', 'price' => '9.99', 'description' => 'This is a test product'));
Of course, there is much to improve.. e.g. you generally don't want to make a query everytime you call fetch() on an entity, but instead only query once and store the resulting object in an IdentityMap, which also ensures data integrity.
Hope this helps, got a bit more than I intended, but I think it's commendable you try to tackle this without throwing a framework on the problem :)