Using $this inside subobject - php

I want to pass $this into the sub objects I create on construct However when I dump it I get a **RECURSION** flag.
<?php
namespace Cortana\Framework\System;
use Cortana\Framework\System\ApiAccessor\CacheApiAccessor;
use Cortana\Framework\System\ApiAccessor\ConfigurationApiAccessor;
use Cortana\Framework\System\ApiAccessor\DatabaseApiAccessor;
use Cortana\Framework\System\ApiAccessor\EventsManagerApiAccessor;
use Cortana\Framework\System\ApiAccessor\FilesystemApiAccessor;
use Cortana\Framework\System\ApiAccessor\LocaleApiAccessor;
use Cortana\Framework\System\ApiAccessor\LogApiAccessor;
use Cortana\Framework\System\ApiAccessor\SessionApiAccessor;
use Cortana\Framework\System\ApiAccessor\TranslateApiAccessor;
/**
* System Handler Instance
*
* #author root
*/
class SystemHandler
{
/**
* Development Environment Flag
*
* #var string
*/
const ENV_DEVELOPMENT = 'DEVELOPMENT';
/**
* Staging Environment Flag
*
* #var string
*/
const ENV_STAGING = 'STAGING';
/**
* Test Environment Flag
*
* #var string
*/
const ENV_TEST = 'TEST';
/**
* Production Environment Flag
*
* #var string
*/
const ENV_PRODUCTION = 'PRODUCTION';
/**
* Configuration api accessor instance
*
* #var ConfigurationApiAccessor
*/
protected $configurationApiAccessor;
/**
* Database api accessor instance
*
* #var DatabaseApiAccessor
*/
protected $databaseApiAccessor;
/**
* Translation api accessor instance
*
* #var TranslateApiAccessor
*/
protected $translateApiAccessor;
/**
* Cache api accessor instance
*
* #var CacheApiAccessor
*/
protected $cacheApiAccessor;
/**
* Locale api accessor instance
*
* #var LocaleApiAccessor
*/
protected $localeApiAccessor;
/**
* Events Manager api accessor instance
*
* #var EventsManagerApiAccessor
*/
protected $eventsManagerApiAccessor;
/**
* Log api accessor instance
*
* #var LogApiAccessor
*/
protected $logApiAccessor;
/**
* Session api accessor instance
*
* #var SessionApiAccessor
*/
protected $sessionApiAccessor;
/**
* Filesystem api accessor instance
*
* #var FilesystemApiAccessor
*/
protected $filesystemApiAccessor;
/**
* Array of Loaded Modules
*
* #var array
*/
protected $loadedModules=[];
/**
* System Environment
*
* #var string
*/
protected $environment;
/**
* System Handler Constructor. Loads Accessors
*/
public function __construct()
{
$this->filesystemApiAccessor = new FilesystemApiAccessor();
$this->configurationApiAccessor = new ConfigurationApiAccessor();
$this->databaseApiAccessor = new DatabaseApiAccessor();
$this->cacheApiAccessor = new CacheApiAccessor();
$this->eventsManagerApiAccessor = new EventsManagerApiAccessor();
$this->localeApiAccessor = new LocaleApiAccessor();
$this->logApiAccessor = new LogApiAccessor();
$this->sessionApiAccessor = new SessionApiAccessor();
$this->translateApiAccessor = new TranslateApiAccessor();
}
public function build()
{
}
/**
* Set the system environment
*
* #param string $environment the system environment
* #return SystemHandler
*/
public function setEnvironment($environment)
{
$this->environment = $environment;
return $this;
}
/**
* Return the system environment
*
* #return string the system environment
*/
public function getEnvironment()
{
return $this->environment;
}
/**
* Set the system loaded modules
*
* #param array $loadedmodules array of loaded system modules
* #return SystemHandler
*/
public function setLoadedModules(array $loadedmodules)
{
$this->loadedModules = $loadedmodules;
return $this;
}
/**
* Add a module as a loaded module
*
* #param string $module module to add as a loaded module
* #return SystemHandler
*/
public function addLoadedModule($module)
{
$this->loadedModules[] = $module;
return $this;
}
/**
* Clear loaded modules
*
* #return SystemHandler
*/
public function clearLoadedModule()
{
$this->loadedModules = [];
return $this;
}
/**
* Set the configuration api accessor instance
*
* #param ConfigurationApiAccessor $configurationApiAccessor the configuration api accessor instance
* #return SystemHandler
*/
public function setConfigurationApiAccessor(ConfigurationApiAccessor $configurationApiAccessor)
{
$this->configurationApiAccessor = $configurationApiAccessor;
return $this;
}
/**
* Return the configuration api accessor instance
*
* #return ConfigurationApiAccessor the configuration api accessor instance
*/
public function getConfigurationApiAccessor()
{
return $this->configurationApiAccessor;
}
/**
* Set the database api accessor instance
*
* #param DatabaseApiAccessor $databaseApiAccessor the database api accessor instance
* #return SystemHandler
*/
public function setDatabaseApiAccessor(DatabaseApiAccessor $databaseApiAccessor)
{
$this->databaseApiAccessor = $databaseApiAccessor;
return $this;
}
/**
* Return the database api accessor instance
*
* #return DatabaseApiAccessor the database api accessor instance
*/
public function getDatabaseApiAccessor()
{
return $this->databaseApiAccessor;
}
/**
* Set the translation api accessor instance
*
* #param TranslateApiAccessor $translateApiAccessor the translation api accessor instance
* #return SystemHandler
*/
public function setTranslateApiAccessor(TranslateApiAccessor $translateApiAccessor)
{
$this->translateApiAccessor = $translateApiAccessor;
return $this;
}
/**
* Return the translation api accessor instance
*
* #return TranslateApiAccessor the translation api accessor instance
*/
public function getTranslateApi()
{
return $this->translateApiAccessor;
}
/**
* Set the cache api accessor instance
*
* #param CacheApiAccessor $cacheApiAccessor the cache api accessor instance
* #return SystemHandler
*/
public function setCacheApiAccessor(CacheApiAccessor $cacheApiAccessor)
{
$this->cacheApiAccessor = $cacheApiAccessor;
return $this;
}
/**
* Return the cache api accessor instance
*
* #return CacheApiAccessor the cache api accessor instance
*/
public function getCacheApiAccessor()
{
return $this->cacheApiAccessor;
}
/**
* Set the locale api accessor instance
*
* #param LocaleApiAccessor $localeApiAccessor the locale api accessor instance
* #return SystemHandler
*/
public function setLocaleApiAccessor(LocaleApi $localeApiAccessor)
{
$this->localeApiAccessor = $localeApiAccessor;
return $this;
}
/**
* Return the locale api accessor instance
*
* #return LocaleApiAccessor the locale api accessor instance
*/
public function getLocaleApiAccessor()
{
return $this->localeApi;
}
/**
* Set the events manager api accessor instance
*
* #param EventsManagerApiAccessor $eventsManagerApiAccessor the events manager api accessor instance
* #return SystemHandler
*/
public function setEventsManagerApiAccessor(EventsManagerApiAccessor $eventsManagerApiAccessor)
{
$this->eventsManagerApiAccessor = $eventsManagerApiAccessor;
return $this;
}
/**
* Return the events manager api accessor instance
*
* #return EventsManagerApiAccessor the events manager api accessor instance
*/
public function getEventsManagerApiAccessor()
{
return $this->eventsManagerApiAccessor;
}
/**
* Set the logger api accessor instance
*
* #param LogApiAccessor $logApiAccessor the log api accessor instance
* #return SystemHandler
*/
public function setLogApi(LogApiAccessor $logApiAccessor)
{
$this->logApiAccessor = $logApiAccessor;
return $this;
}
/**
* Return the log api accessor instance
*
* #return LogApiAccessor the log api accessor instance
*/
public function getLogApi()
{
return $this->logApiAccessor;
}
/**
* Set the session api accessor instance
*
* #param SessionApiAccessor $sessionApiAccessor the session api accessor instance
* #return SystemHandler
*/
public function setSessionApiAccessor(SessionApiAccessor $sessionApiAccessor)
{
$this->sessionApiAccessor = $sessionApiAccessor;
return $this;
}
/**
* Return the session api accessor instance
*
* #return SessionApiAccessor the session api accessor instance
*/
public function getSessionApiAccessor()
{
return $this->sessionApiAccessor;
}
/**
* Set the filesystem api accessor instance
*
* #param FilesystemApiAccessor $filesystemApiAccessor the filesystem api accessor instance
* #return SystemHandler
*/
public function setFilesystemApiAccessor(FilesystemApiAccessor $filesystemApiAccessor)
{
$this->filesystemApiAccessor = $filesystemApiAccessor;
return $this;
}
/**
* Return the filesystem api accessor instance
*
* #return FilesystemApiAccessor the filesystem api accessor instance
*/
public function getFilesystemApiAccessor()
{
return $this->filesystemApiAccessor;
}
}

when you create a new class that extends other class, you don't need to pass anything to the constructor, it will be automatically available in the subclass as long as those methods/properties are public or protected. If they are private, you cannot use them in subclass. If you want to use protected/public properties or methods from parent class in your subclass, use them as if you were in the parent class.
class parent{
protected $foo = 'foobar';
protected function get_foo(){
return $this->foo;
}
}
class subclass extends parent{
protected $bar;
public function __construct(){
$this->bar = $this->get_foo();
}
public function get_bar(){
return $this->bar;
}
}
$baz = new bar;
echo $baz->get_bar();
// outputs 'foobar'

Related

Facade Pattern Not Resolving Bind Property if app isn't initialize

Currently, I am working on Facade Pattern which doesn't resolve through the application main Container class where i have implement bind method through the interface.
Index.php
use Bundles\Support\Facades\Route;
Route::get('/users', function () {
return 'Hello';
});
Facade:
Route facade
namespace Bundles\Support\Facades;
class Route extends Facade
{
/**
* Get the registered name of the component.
*
* #return string
*/
protected static function getFacadeAccessor()
{
return 'route';
}
}
Facade.php
namespace Bundles\Support\Facades;
use Bundles\Foundations\Application;
use RuntimeException;
class Facade
{
/**
* The application instance being facaded.
*/
protected static Application $app;
/**
* The resolved object instances.
*
* #var array
*/
protected static $resolvedInstance;
/**
* Indicates if the resolved instance should be cached.
*
* #var bool
*/
protected static $cached = true;
/**
* #param $method
* #param $args
*/
public static function __callStatic($method, $args)
{
$instance = static::getFacadeRoot();
if (!$instance) {
throw new RuntimeException('A facade root has not been set.');
}
var_dump($method, $args, static::getFacadeAccessor());
Application::$app->bind(static::getFacadeAccessor(), $args, $method);
exit;
}
/**
* Get the root object behind the facade.
*
* #return mixed
*/
public static function getFacadeRoot()
{
return static::resolveFacadeInstance(static::getFacadeAccessor());
}
/**
* Get the registered name of the component.
*
* #throws \RuntimeException
*
* #return string
*/
protected static function getFacadeAccessor()
{
throw new RuntimeException('Facade does not implement getFacadeAccessor method.');
}
/**
* Resolve the facade root instance from the container.
*
* #param string $name
*
* #return mixed
*/
protected static function resolveFacadeInstance($name)
{
if (isset(static::$resolvedInstance[$name])) {
return static::$resolvedInstance[$name];
}
if (static::$app) {
if (static::$cached) {
return static::$resolvedInstance[$name] = static::$app[$name];
}
return static::$app[$name];
}
}
}
Container.php
namespace Bundles\Containers;
use Bundles\Foundations\Application;
use Bundles\Interfaces\Application as ApplicationContract;
use Bundles\Routes\Route;
class Container implements ApplicationContract
{
/**
* #var Route
*/
public Route $router;
/**
* #var Application
*/
public static Application $app;
// register method called in Application constructor
public function register()
{
$this->router = new Route();
}
/**
* > The `bind` function takes a string as the first argument, and a second argument that can be either a string or a
* closure.
*
* #param string abstract The abstract type to bind to
* #param concrete the class name or a closure that returns an instance of the class
* #param shared whether the object should be shared or not
* #param null|mixed $concrete
* #param mixed $shared
*/
public function bind(string $abstract, $concrete = null, $shared = ''): void
{
var_dump($abstract, $concrete);
exit;
}
}
anyone can help on this?
Thanks in advance

Creating a new entity which contains another existing entity

I have the following two tables and corresponding two entities shown at the bottom of this post. time_unit only consists of several preset records which are s/second/1, m/minute/60, h/hour/360, etc.
I need to create a new Schedule. While not shown, I have several types of schedules which use the provided data differently and as such wish to place the setters inside the entity (either the constructor or some interface method) instead of in the service. To create the new schedule, I execute $scheduleService->create(['name'=>'the schedule name', 'other_data'=>123, 'time_unit'=>'h']);.
<?php
namespace Michael\App\Service;
use Michael\App\Entity;
class ScheduleService
{
public function create(array $params):int {
//validation as applicable
$schedule=new Entity\Schedule($params);
$this->em->persist($schedule);
$this->em->flush();
return $schedule->getId();
}
}
And then add the following constructor in the Schedule entity:
public function __construct(array $params) {
$this->setName($params['name']);
$this->setOtherData($params['other_data']);
$timeUnit=new TimeUnit();
$timeUnit->setUnit($params['time_unit']);
$this->setTimeUnit($timeUnit);
}
But this will not work because I am creating a new instance of TimeUnit and Doctrine will complain.
As an alternative, I can pass Schedule the entity manager, but everything I've read states that doing so is bad practice.
How should one create a new entity which contains another existing entity?
Schema and basic entities without additional logic are shown below:
CREATE TABLE schedule (id INT NOT NULL, time_unit VARCHAR(1) NOT NULL, name VARCHAR(45) NOT NULL, other_data VARCHAR(45) NOT NULL, INDEX fk_schedule_time_unit_idx (time_unit), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE = InnoDB;
CREATE TABLE time_unit (unit VARCHAR(1) NOT NULL, name VARCHAR(45) NOT NULL, seconds INT NOT NULL, PRIMARY KEY(unit)) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE = InnoDB;
ALTER TABLE schedule ADD CONSTRAINT FK_5A3811FB7106057E FOREIGN KEY (time_unit) REFERENCES time_unit (unit);
schedule.php
<?php
namespace Michael\App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Schedule
*
* #ORM\Table(name="schedule", indexes={#ORM\Index(name="fk_schedule_time_unit_idx", columns={"time_unit"})})
* #ORM\Entity
*/
class Schedule
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="NONE")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=45)
*/
private $name;
/**
* #var string
*
* #ORM\Column(name="other_data", type="string", length=45)
*/
private $other_data;
//Not included since docs state one shouldn't map foreign keys to fields in an entity
//private $time_unit;
/**
* #var \TimeUnit
*
* #ORM\ManyToOne(targetEntity="TimeUnit")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="time_unit", referencedColumnName="unit")
* })
*/
private $timeUnit;
/**
* Set id.
*
* #param int $id
*
* #return Schedule
*/
public function setId($id)
{
$this->id = $id;
return $this;
}
/**
* Get id.
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set name.
*
* #param string $name
*
* #return Schedule
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name.
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set otherData.
*
* #param string $otherData
*
* #return Schedule
*/
public function setOtherData($otherData)
{
$this->other_data = $otherData;
return $this;
}
/**
* Get otherData.
*
* #return string
*/
public function getOtherData()
{
return $this->other_data;
}
/**
* Set timeUnit.
*
* #param TimeUnit $timeUnit (not a string)
*
* #return Schedule
*/
public function setTimeUnit($timeUnit)
{
$this->timeUnit = $timeUnit;
return $this;
}
/**
* Get timeUnit.
*
* #return TimeUnit (not a string)
*/
public function getTimeUnit()
{
return $this->timeUnit;
}
}
time_unit.php
<?php
namespace Michael\App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* TimeUnit
*
* #ORM\Table(name="time_unit")
* #ORM\Entity
*/
class TimeUnit
{
/**
* #var string
*
* #ORM\Column(name="unit", type="string", length=1)
* #ORM\Id
* #ORM\GeneratedValue(strategy="NONE")
*/
private $unit;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=45)
*/
private $name;
/**
* #var int
*
* #ORM\Column(name="seconds", type="integer")
*/
private $seconds;
/**
* Set unit.
*
* #param string $unit
*
* #return TimeUnit
*/
public function setUnit($unit)
{
$this->unit = $unit;
return $this;
}
/**
* Get unit.
*
* #return string
*/
public function getUnit()
{
return $this->unit;
}
/**
* Set name.
*
* #param string $name
*
* #return TimeUnit
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name.
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set seconds.
*
* #param int $seconds
*
* #return TimeUnit
*/
public function setSeconds($seconds)
{
$this->seconds = $seconds;
return $this;
}
/**
* Get seconds.
*
* #return int
*/
public function getSeconds()
{
return $this->seconds;
}
}
Passing EntityManager to entities is a bad practice because entities in Doctrine are used as data objects and hence should contain minimum amount of logic. All application logic related to entities should be moved to either custom repositories or to separate classes that belongs to application's service layer.
In your case you need to either pass instance of TimeUnit directly to constructor without attempting to construct it inside entity or expect it to be set through setter method.
Instead you need to modify your ScheduleService::create() to allow entity creation logic to be customizable. Since your ScheduleService basically implements Factory method pattern you need to make one step further towards implementation of Abstract factory pattern.
Abstract factory basically relies on list of concrete factories that are responsible for construction of concrete class instances instead of attempting to include all possible logic inside itself. Please find below example of implementation of such pattern in your case. It may look overcomplicated because I've extracted 2 interfaces and abstract class and this scheme can be simplified by use of 2 separate interfaces allows abstract and concrete factories to share common base while retaining necessary differences. Abstract class for concrete factories is used to allow extraction of basic entity configuration logic to avoid code duplication.
/**
* Interface for Schedule entity factories
*/
interface AbstractScheduleFactoryInterface
{
/**
* Create schedule entity by given params
*
* #param array $params
* #return Schedule
*/
public function create(array $params = []): Schedule;
}
/**
* Interface for concrete Schedule entity factories
*/
interface ScheduleFactoryInterface extends AbstractScheduleFactoryInterface
{
/**
* Decide if this factory can create schedule entity with given params
*
* #param array $params
* #return bool
*/
public function canCreate(array $params): bool;
}
/**
* Implementation of "Abstract Factory" pattern that relies on concrete factories for constructing Schedule entities
*/
class ScheduleFactory implements AbstractScheduleFactoryInterface
{
/**
* #var ScheduleFactoryInterface[]
*/
private $factories;
/**
* #param ScheduleFactoryInterface[] $factories
*/
public function __construct(array $factories)
{
$this->factories = $factories;
}
/**
* {#inheritdoc}
*/
public function create(array $params = []): Schedule
{
// Select factory that is able to create Schedule entity by given params
/** #var ScheduleFactoryInterface $factory */
$factory = array_reduce($this->factories, function (?ScheduleFactoryInterface $selected, ScheduleFactoryInterface $current) use ($params) {
if ($selected) {
return $selected;
}
return $current->canCreate($params) ? $current : null;
});
if (!$factory) {
// We have no factory to construct Schedule entity by given params
throw new \InvalidArgumentException('Unable to construct Schedule entity by given params');
}
// Construct entity by using selected concrete factory
return $factory->create($params);
}
}
/**
* Base implementation of concrete Schedule entity factory
* to allow sharing some common code between factories
*/
abstract class AbstractScheduleFactory implements ScheduleFactoryInterface
{
/**
* Basic entity configuration to avoid code duplication in concrete factories
*
* #param Schedule $entity
* #param array $params
*/
protected function configure(Schedule $entity, array $params = []): void
{
// This code is more or less copied from your code snippet
$entity->setName($params['name'] ?? '');
$entity->setOtherData($params['other_data'] ?? '');
}
}
/**
* Example implementation of Schedule entity factory with Schedules with TimeUnit
*/
class TimeUnitScheduleFactory extends AbstractScheduleFactory
{
/**
* #var EntityManager
*/
private $em;
/**
* #param EntityManager $em
*/
public function __construct(EntityManager $em)
{
$this->em = $em;
}
/**
* {#inheritdoc}
*/
public function canCreate(array $params): bool
{
return array_key_exists('time_unit', $params);
}
/**
* Create schedule entity by given params
*
* #param array $params
* #return Schedule
* #throws \RuntimeException
*/
public function create(array $params = []): Schedule
{
$schedule = new Schedule();
// Perform basic Schedule configuration using shared base code
$this->configure($schedule, $params);
try {
// Attempt to assign time unit
$timeUnit = $this->em->find(TimeUnit::class, $params['time_unit']);
if (!$timeUnit instanceof TimeUnit) {
// No TimeUnit is available in database - create one
$timeUnit = new TimeUnit();
$timeUnit->setUnit($params['time_unit']);
$this->em->persist($timeUnit);
}
$schedule->setTimeUnit($timeUnit);
} catch (ORMException $e) {
throw new \RuntimeException('Failed to get TimeUnit entity', 0, $e);
}
return $schedule;
}
}
As you can see - this scheme allows you to have arbitrary amount of concrete factories for Schedule entities that needs to be passed to ScheduleFactory as constructor argument. After that ScheduleFactory::create() can be used to create any kind of Schedule entities with different construction logic.

Phpunit partial mock + proxy Entity

I tried find solution to my issue but didn't find anything.
I use: Symfony, Doctrine, PhpUnit
I have one entity class InvoiceNumerator:
/**
* InvoiceNumerator
*
* #ORM\Table(name="invoice_numerator")
* #ORM\Entity(repositoryClass="AppBundle\Repository\InvoiceNumeratorRepository")
*/
class InvoiceNumerator
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="translatedFormat", type="string", length=64)
*/
private $translatedFormat;
/**
* #var int
*
* #ORM\Column(name="currentValue", type="integer", options={"default": 0})
*/
private $currentValue = 0;
/**
* #var string
*
* #ORM\Column(name="current_number", type="string", length=64)
*/
private $currentNumber = '';
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set translatedFormat
*
* #param string $translatedFormat
*
* #return InvoiceNumerator
*/
public function setTranslatedFormat($translatedFormat)
{
$this->translatedFormat = $translatedFormat;
return $this;
}
/**
* Get translatedFormat
*
* #return string
*/
public function getTranslatedFormat()
{
return $this->translatedFormat;
}
/**
* Set currentValue
*
* #param integer $currentValue
*
* #return InvoiceNumerator
*/
public function setCurrentValue($currentValue)
{
$this->currentValue = $currentValue;
return $this;
}
/**
* Get currentValue
*
* #return int
*/
public function getCurrentValue()
{
return $this->currentValue;
}
/**
* #return string
*/
public function getCurrentNumber(): string
{
return $this->currentNumber;
}
/**
* #param string $currentNumber
* #return InvoiceNumerator
*/
public function setCurrentNumber(string $currentNumber): InvoiceNumerator
{
$this->currentNumber = $currentNumber;
return $this;
}
}
and I need in my tests to mock this class, but my setters should be left the same - no stubs - working code.
To mock this class, I made simple mock method:
public function getInvoiceNumerator()
{
$invoiceNumerator = $this->createMock(InvoiceNumerator::class);
$invoiceNumerator->method('getTranslatedFormat')
->willReturn('FS-CM/{n}/2018/01');
$invoiceNumerator->method('getCurrentValue')
->willReturn('1');
$invoiceNumerator->method('getCurrentNumber')
->willReturn('FS-CM/1/2018/01');
return $invoiceNumerator;
}
but in this case my setters are not working.
I can also set values on new Entity object:
public function getInvoiceNumerator()
{
$invoiceNumerator = new InvoiceNumerator();
$invoiceNumerator->setTranslatedFormat('FS-CM/{n}/2018/01');
$invoiceNumerator->setCurrentValue(1);
$invoiceNumerator->setCurrentNumber('FS-CM/1/2018/01');
return $invoiceNumerator;
}
In this case my setters working properly.
Question:
Is there any better way to do this? What is the best practice?
You almost have the answer in your question “Phpunit partial mock + proxy Entity”: there is a createPartialMock() method which you can use like this:
$invoiceNumerator = $this-> createPartialMock(
InvoiceNumerator::class,
['nameOfMockedMethod1', 'nameOfMockedMethod2']
);
This method has been available in PHPUnit 5.5 and newer. If you are using an older version, you can use setMethods(), but have to call it on the result returned by getMockBuilder(), not on the object returned by createMock() (which is the reason of the error you got after trying the approach from the 1st answer):
$subject = $this->getMockBuilder(MyClass::class)
->setMethods(['method1', 'method2'])
->getMock();
However, please note that createPartialMock() does slightly more. For instance, it will automatically disable the original constructor – which is almost always what you want in your tests (and what you have to do explicitly when using setMethods()). See documentation for exact information.
Basically you can set your mock to only mock specific methods:
$invoiceNumerator = $this->getMockBuilder(InvoiceNumerator::class)
->setMethods(["getTranslatedFormat","getCurrentValue", "getCurrentNumber"])
->getMock();
according to the documentation
setMethods(array $methods) can be called on the Mock Builder object to specify the methods that are to be replaced with a configurable test double. The behavior of the other methods is not changed. If you call setMethods(null), then no methods will be replaced.
Update:
Since PHPUnit 8 the setMethods function has been deprecated and replaced with onlyMethods for methods that exist on the mocked class and addMethods for methods that don't yet exist on the mock class (e.g. they will be implemented in the future but you want to text their dependencies assuming they already exist).

How to extend a vendor model abstract class into an entity

based on the way to do it from FOSUserbundle, i made a vendor bundle that contains a Analytic Abstrakt class than implements an AnalyticInterface and in a src/AppBundle, i(ve got a Analytic entity that extends the vendor Analytic abstract class but that doesn't work, my entity doesn't the abstract class properties ($referer, $host, $ip, $browser, ...)
The interface:
<?php
/**
* Created by PhpStorm.
* User: VanIllaSkyPE
* Date: 01/11/2016
* Time: 23:26
*/
namespace Gkratz\AnalyticBundle\Model;
/**
* Interface AnalyticInterface
* #package Gkratz\AnalyticBundle\Model
*/
interface AnalyticInterface
{
/**
* Set date
*
* #param \DateTime $date
* #return Analytic
*/
public function setDate($date);
/**
* Get date
*
* #return \DateTime
*/
public function getDate();
/**
* Set page
*
* #param string $page
* #return Analytic
*/
public function setPage($page);
/**
* Get page
*
* #return string
*/
public function getPage();
/**
* Set ip
*
* #param string $ip
* #return Analytic
*/
public function setIp($ip);
/**
* Get ip
*
* #return string
*/
public function getIp();
/**
* Set host
*
* #param string $host
* #return Analytic
*/
public function setHost($host);
/**
* Get host
*
* #return string
*/
public function getHost();
/**
* Set browser
*
* #param string $browser
* #return Analytic
*/
public function setBrowser($browser);
/**
* Get browser
*
* #return string
*/
public function getBrowser();
/**
* Set referer
*
* #param string $referer
* #return Analytic
*/
public function setReferer($referer);
/**
* Get referer
*
* #return string
*/
public function getReferer();
/**
* Set newSession
*
* #param boolean $newSession
* #return Analytic
*/
public function setNewSession($newSession);
/**
* Get newSession
*
* #return boolean
*/
public function getNewSession();
}
The base abstract class:
<?php
/**
* Created by PhpStorm.
* User: VanIllaSkyPE
* Date: 01/11/2016
* Time: 22:30
*/
namespace Gkratz\AnalyticBundle\Model;
/**
* Class Analytic
* #package Gkratz\AnalyticBundle\Model
*/
abstract class Analytic implements AnalyticInterface
{
protected $id;
/**
* #var \DateTime
*/
protected $date;
/**
* #var string
*/
protected $page;
/**
* #var string
*/
protected $ip;
/**
* #var string
*/
protected $host;
/**
* #var string
*/
protected $browser;
/**
* #var string
*/
protected $referer;
/**
*
* #var boolean
*/
protected $newSession;
public function __construct()
{
$this->date = new \Datetime();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set date
*
* #param \DateTime $date
* #return Analytic
*/
public function setDate($date)
{
$this->date = $date;
return $this;
}
/**
* Get date
*
* #return \DateTime
*/
public function getDate()
{
return $this->date;
}
/**
* Set page
*
* #param string $page
* #return Analytic
*/
public function setPage($page)
{
$this->page = $page;
return $this;
}
/**
* Get page
*
* #return string
*/
public function getPage()
{
return $this->page;
}
/**
* Set ip
*
* #param string $ip
* #return Analytic
*/
public function setIp($ip)
{
$this->ip = $ip;
return $this;
}
/**
* Get ip
*
* #return string
*/
public function getIp()
{
return $this->ip;
}
/**
* Set host
*
* #param string $host
* #return Analytic
*/
public function setHost($host)
{
$this->host = $host;
return $this;
}
/**
* Get host
*
* #return string
*/
public function getHost()
{
return $this->host;
}
/**
* Set browser
*
* #param string $browser
* #return Analytic
*/
public function setBrowser($browser)
{
$this->browser = $browser;
return $this;
}
/**
* Get browser
*
* #return string
*/
public function getBrowser()
{
return $this->browser;
}
/**
* Set referer
*
* #param string $referer
* #return Analytic
*/
public function setReferer($referer)
{
$this->referer = $referer;
return $this;
}
/**
* Get referer
*
* #return string
*/
public function getReferer()
{
return $this->referer;
}
/**
* Set newSession
*
* #param boolean $newSession
* #return Analytic
*/
public function setNewSession($newSession)
{
$this->newSession = $newSession;
return $this;
}
/**
* Get newSession
*
* #return boolean
*/
public function getNewSession()
{
return $this->newSession;
}
}
the App analytic entity:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Gkratz\AnalyticBundle\Model\Analytic as BaseAnalytic;
/**
* Analytic
*
* #ORM\Table(name="analytic")
* #ORM\Entity(repositoryClass="AppBundle\Repository\AnalyticRepository")
*/
class Analytic extends BaseAnalytic
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
public function __construct()
{
parent::__construct();
}
}
Thank you for any response
The abstract class Analytic defines only properties.
Analytic needs to annotate them with #ORM\Column(...) if you want to use them as DB columns.
Also see: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/inheritance-mapping.html

->find($id) in entity returns an empty proxy object symfony2

I'm doing a search on an entity, but this returns me a proxy object. Thus I am trying to do the search.
$em = $this->getDoctrine()->getEntityManager();
$tipou = $em->getRepository('SertecomvendoautosBundle:TipoUsuario')->find(5);
In the database, table tipo_usuario I have only two records with ID 4 and 5, but to make the search with ID 4, doctrine returns the object for me and not the proxy object that returns me with ID 5.
$em = $this->getDoctrine()->getEntityManager();
$tipou = $em->getRepository('SertecomvendoautosBundle:TipoUsuario')->find(4);
This brings the object in the normal way.
Really do not understand this behavior that doctrine, I would like to know what happens with this.
This is code of entity tipo_usuario.
class TipoUsuario {
/**
* #var string $tipouNombre
*/
private $tipouNombre;
/**
* #var string $tipouToken
*/
private $tipouToken;
/**
* #var datetime $tipouCreatedAt
*/
private $tipouCreatedAt;
/**
* #var datetime $tipouUpdatedAt
*/
private $tipouUpdatedAt;
/**
* #var integer $tipouId
*/
private $tipouId;
/**
* Set tipouNombre
*
* #param string $tipouNombre
*/
public function setTipouNombre($tipouNombre)
{
$this->tipouNombre = $tipouNombre;
}
/**
* Get tipouNombre
*
* #return string
*/
public function getTipouNombre()
{
return $this->tipouNombre;
}
/**
* Set tipouToken
*
* #param string $tipouToken
*/
public function setTipouToken($tipouToken)
{
$this->tipouToken = $tipouToken;
}
/**
* Get tipouToken
*
* #return string
*/
public function getTipouToken()
{
return $this->tipouToken;
}
/**
* Set tipouCreatedAt
*
* #param datetime $tipouCreatedAt
*/
public function setTipouCreatedAt($tipouCreatedAt)
{
$this->tipouCreatedAt = $tipouCreatedAt;
}
/**
* Get tipouCreatedAt
*
* #return datetime
*/
public function getTipouCreatedAt()
{
return $this->tipouCreatedAt;
}
/**
* Set tipouUpdatedAt
*
* #param datetime $tipouUpdatedAt
*/
public function setTipouUpdatedAt($tipouUpdatedAt)
{
$this->tipouUpdatedAt = $tipouUpdatedAt;
}
/**
* Get tipouUpdatedAt
*
* #return datetime
*/
public function getTipouUpdatedAt()
{
return $this->tipouUpdatedAt;
}
/**
* Get tipouId
*
* #return integer
*/
public function getTipouId()
{
return $this->tipouId;
}

Categories