I would like to make a test unit using constraint but I have this error when running my test
This are my different classes and the obtaining error after running phpunit
use Symfony\Component\Validator\Constraint;
/**
* #Annotation
*/
class Age18 extends Constraint
{
public $message = 'Vous devez avoir 18 ans.';
}
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
class Age18Validator extends ConstraintValidator
{
public function validate($dateNaissance, Constraint $constraint)
{
if ($dateNaissance > new \DateTime("18 years ago"))
{
$this->context->addViolation($constraint->message);
}
}
}
use Symfony\Component\Translation\TranslatorInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
class Age18ValidatorTest extends \PHPUnit_Framework_TestCase
{
private $constraint;
public function setUp()
{
$this->constraint = $this->getMock('Symfony\Component\Validator\Constraint');
}
public function testValidate()
{
/*ConstraintValidator*/
$validator = new Age18Validator();
$context = $this
->getMockBuilder('Symfony\Component\Validator\ExecutionContext')
->disableOriginalConstructor()
->getMock('Age18Validator', array('validate'));
$context->expects($this->once())
->method('addViolation')
->with('Vous devez avoir 18 ans.');
$validator->initialize($context);
$validator->validate('10/10/2000', $this->constraint);
}
public function tearDown()
{
$this->constraint = null;
}
}
Expectation failed for method name is equal to <string:addViolation> when invoked 1 time(s).
Method was expected to be called 1 times, actually called 0 times.
Please could you help me to solve this problem?
Thanks you!!
Check the type of your element: in the validator class you use the comparator between two DateTime object but in the test you pass a string to the validator.
This is my test class:
namespace Acme\DemoBundle\Tests\Form;
use Acme\DemoBundle\Validator\Constraints\Age18;
use Acme\DemoBundle\Validator\Constraints\Age18Validator;
class Age18ValidatorTest extends \PHPUnit_Framework_TestCase
{
private $constraint;
private $context;
public function setUp()
{
$this->constraint = new Age18();
$this->context = $this->getMockBuilder('Symfony\Component\Validator\ExecutionContext')->disableOriginalConstructor()->getMock();
}
public function testValidate()
{
/*ConstraintValidator*/
$validator = new Age18Validator();
$validator->initialize( $this->context);
$this->context->expects($this->once())
->method('addViolation')
->with($this->constraint->message,array());
$validator->validate(\Datetime::createFromFormat("d/m/Y","10/10/2000"), $this->constraint);
}
public function tearDown()
{
$this->constraint = null;
}
}
Hope this help
Related
I have a simple service on PHP with this structure:
Controller
ChannelControllerAPI.php
Service
ChannelService.php
Model
Repository
ChannelRepositoryInterface.php
RedisChannelRepository.php // implements ChannelRepositoryInterface
SqlChannelRepository.php // implements ChannelRepositoryInterface
HybridChannelRepository.php // implements ChannelRepositoryInterface
AbstractChannel.php
BusinessChannel.php // extends AbstractChannel
UserChannel.php // extends AbstractChannel
And I use DI container to resolve dependencies.
API controller code:
class ChannelControllerApi extends Controller
{
private $channelService;
public function __construct(ChannelService $channelService)
{
$this->channelService = $channelService;
}
public function getChannelInfo()
{
$channelId = $ths->request->Post('channelId');
$channelType = $ths->request->Post('channelType');
$result = $channelService->getChannelInfo($channelId, $channelType);
// return API request
}
}
I get ChannelService in Controller Api __construct with DI;
ChannelService code:
class ChannelService
{
private $channelRepository;
public function __construct(ChannelRepository $channelRepository)
{
$this->channelRepository = $channelRepository;
}
public function getChannelInfo(int $channelId, string $channelType)
{
return $channelRepository ->findRules($channelId, $channelType);
}
}
ChannelRepository code for example SQLRepository:
use channel/Models/AbstractChannel;
class SqlChannelRepository
{
/**
* Channel $model (ActiveRecord or DTO Model)
*/
private $model;
public function __construct(AbstractChannel $model)
{
$this->model= $model;
}
public function find(int $channelId, string $channelType) : Channel
{
return $this->model->where('id', $channelId)->where('type', $channelType)->getModel();
}
}
Abstract channel model class code:
abstract class Channel extends ActiveRecordModel
{
protected $tableName = 'channel_business';
protected $fildmap = [
'id',
'type',
// ...
];
}
BusinessChannel model class code:
class Channel extends AbstractChannel;
{
protected $tableName = 'channel_business';
}
UserChannel model class code:
class UserChannel extends AbstractChannel;
{
protected $tableName = 'channel_user';
}
I don't want to pass $channelId and $type to Service or to Repository.
How? and in which layer can I pass the right model object to SqlChannelRepository?
Where should this logic be placed:
if ($requset->Post('type') == 'user') {
$model = new UserChannel();
// or setting in DI container: app()->container::set('UserChannel');
} elseif ($requset->Post('type') == 'business') {
$model = new BusinessChannel();
} elseif .... {
......
}
Please help, which pattern or architect solution (can I use to create right ChannelModel by request ($_POST) params and pass it to my repository)?
Sorry for my poor English:)
I have been creating web application using symfony 3.4, I want to use EntityMerger inside PATCH method in the controller.
When I inject the EntityMerger in the constructor of the controller it takes the value null in the function patchMovieAction.
namespace AppBundle\Entity;
use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\ORM\Mapping\Id;
class EntityMerger
{
private $annotationReader;
public function __constructor(AnnotationReader $annotationReader)
{
$this->annotationReader = $annotationReader;
}
public function merge($entity, $changes): void
{
....
}
}
the Controller:
class MoviesController extends AbstractController
{
use ControllerTrait;
private $entityMerger;
public function __constructor(EntityMerger $entityMerger)
{
$this->entityMerger = $entityMerger;
}
public function patchMovieAction(Movie $movie, Movie $modifiedMovie, ConstraintViolationListInterface $validationErrors)
{
if(null ===$movie) {
return $this->view(null,404);
}
if(count($validationErrors)>0){
throw new ValidationException($validationErrors);
}
//Merge entities
$this->entityMerger->merge($movie,$modifiedMovie);
//Persist
$em = $this->getDoctrine()->getManager();
$em->persist($movie);
$em->flush();
//Return
return $movie;
}
When I try to run php bin/console debug:router, I get the error
Cannot autowire service "AppBundle\Controller\MoviesController": argument "$entityMerger" of method "__construct()" references class "AppBundle\Entity\EntityMerger" but
no such service exists.
I'm making some kind of market site with Zend Framework 2. The home got a slider showing all the products (realized with CSS3 keyframes) and some text. Both the sliding pictures and the text are read from a MySQL database. But as result, i get no output but also no errors. The slider gets as many pictures as database rows, but still no content is echoed; plus if i try to change things (like db credentials or getter functions in model) it throws errors as expected, so it clearly reads the db and the problem is elsewhere.
Db for text has 3 fields:
id
name
text
Model for text (Home.php; there's an HomeInterface.php defining all the functions)
<?php
namespace Site\Model;
class Home implements HomeInterface {
protected $id;
protected $name;
protected $text;
public function getId() {
return $this->id;
}
public function getName() {
return $this->name;
}
public function getText() {
return $this->text;
}
}
?>
Mapper for text
<?php
namespace Site\Mapper;
use Site\Model\HomeInterface;
use Zend\Db\Adapter\AdapterInterface;
use Zend\Db\Adapter\Driver\ResultInterface;
use Zend\Stdlib\Hydrator\HydratorInterface;
use Zend\Db\ResultSet\HydratingResultSet;
use Zend\Db\Sql\Sql;
use Zend\Stdlib\Hydrator\ClassMethods;
class TextMapper implements TextMapperInterface {
protected $homePrototype;
protected $adapter;
protected $hydrator;
public function __construct(AdapterInterface $adapter, HomeInterface $homePrototype, HydratorInterface $hydrator) {
$this->adapter = $adapter;
$this->homePrototype = $homePrototype;
$this->hydrator = $hydrator;
}
public function find($name) {
$sql = new Sql($this->adapter);
$select = $sql->select();
$select->from("mono");
$select->where(array("name = ?" => $name));
$stmt = $sql->prepareStatementForSqlObject($select);
$result = $stmt->execute();
if ($result instanceof ResultInterface && $result->isQueryResult() && $result->getAffectedRows()) {
return $this->hydrator->hydrate($result->current(), $this->homePrototype);
}
throw new \InvalidArgumentException("{$name} non esiste.");
}
}
?>
Mapper for text has a factory, since it has dependencies:
<?php
namespace Site\Factory;
use Site\Mapper\TextMapper;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Site\Model\Home;
use Zend\Stdlib\Hydrator\ClassMethods;
class TextMapperFactory implements FactoryInterface {
public function createService(ServiceLocatorInterface $serviceLocator) {
return new TextMapper($serviceLocator->get("Zend\Db\Adapter\Adapter"), new Home(), new ClassMethods(false));
}
}
?>
Service for text:
<?php
namespace Site\Service;
use Site\Model\Home;
use Site\Model\HomeInterface;
use Site\Mapper\TextMapperInterface;
class HomeService implements HomeServiceInterface {
protected $textMapper;
public function __construct (TextMapperInterface $textMapper) {
$this->textMapper = $textMapper;
}
public function findText($name) {
return $this->textMapper->find($name);
}
}
?>
Factory for this service:
<?php
namespace Site\Factory;
use Site\Service\HomeService;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class HomeServiceFactory implements FactoryInterface {
public function createService(ServiceLocatorInterface $serviceLocator) {
$textMapper = $serviceLocator->get("Site\Mapper\TextMapperInterface");
return new HomeService($textMapper);
}
}
?>
Controller
<?php
namespace Site\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Site\Service\HomeServiceInterface;
use Zend\View\Model\ViewModel;
class SkeletonController extends AbstractActionController {
protected $homeService;
public function __construct(HomeServiceInterface $homeService) {
$this->homeService = $homeService;
}
public function indexAction() {
return new ViewModel(array (
"home" => $this->homeService->findText("home")
));
}
}
?>
Finally, the view:
<?php echo $this->home->getText(); ?>
Code for slider is similar and both the parts of this page are likely having the same problem. As i said, db is detected, tables and columns too, they aren't empty but nothing gets echoed. Interfaces are properly written, defining all the functions. All views are in the Site\view\Site\Skeleton folder. Any clues about where the problem is? Thank you.
Your code looks good. The only issue I can see is that you are using the ClassMethods hydrator and you have no setters on your entity.
The hydrator will use the entity API to hydrate the entity, if the setId, setName or setText are not callable then the values will not be set.
Although I recommend adding the missing methods you can also use the Zend\Stdlib\Hydrator\Reflection to set the entity properties without setters (via SPL ReflectionProperty)
I have created a View Helper to display latest Adverts from a Database Table. Since I have different Types of Adverts, I would like to be able to pass a variable from inside my View where I call the View Helper to show specific Adverts.
I am sorry that I can not explain it in a better way, but I am still a total beginner in ZF2. I will add the Sourcecode and hopefully this will make it more clear. Please note that I have the Sourcecode from a Book which displayed Pizza's randomly and changed it till it worked to show my adverts. I might still have Code in it which is not actually needed, so please do not wonder... Okay here the code:
1. the view: index.html
<?php foreach ($this->latestAdvert() as $value){ ?>
<li><?php echo $value->getAdvertTitle();?></li>
<?php }?>
2. the view Helper: Advert\View\Helper\LatestAdvert.php
namespace Advert\View\Helper;
use Zend\View\Helper\AbstractHelper;
class LatestAdvert extends AbstractHelper
{
protected $random = null;
public function __construct($random)
{
$this->setLatestAdvert($random);
}
public function setLatestAdvert($random)
{
$this->random = $random;
}
public function getLatestAdvert()
{
return $this->random;
}
public function __invoke()
{
$latestAdverts = $this->getLatestAdvert();
return $latestAdverts;
}
}
3. the Factory: Advert\View\Helper\LatestAdvertFactory.php
namespace Advert\View\Helper;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class LatestAdvertFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
$locator = $serviceLocator->getServiceLocator();
$service = $locator->get('Advert\Service');
$random = $service->fetchSingleByRandom();
$helper = new LatestAdvert($random);
return $helper;
}
}
4. the Service: Advert\Service\LatestAdvertService .php
namespace Advert\Service;
use Advert\Entity\Advert as AdvertEntity;
use Doctrine\ORM\EntityManager;
use Zend\ServiceManager\ServiceManager;
use Zend\ServiceManager\ServiceManagerAwareInterface;
use Zend\Debug\Debug;
class LatestAdvertService implements ServiceManagerAwareInterface
{
/**
* Service manager.
* #var Zend\ServiceManager\ServiceManager
*/
private $serviceManager = null;
/**
* Sets service manager.
* #param Zend\ServiceManager\ServiceManager $serviceManager Service manager.
*/
public function setServiceManager(ServiceManager $serviceManager)
{
$this->serviceManager = $serviceManager;
}
/**
* Returns service manager.
* #return type
*/
public function getServiceLocator()
{
return $this->serviceManager;
}
public function fetchSingleByRandom()
{
// Get Doctrine entity manager.
$entityManager = $this->getServiceLocator()
->get('doctrine.entitymanager.orm_default');
$advertType = 'wanted'; // This should be removed
$random = $entityManager->getRepository('Advert\Entity\Advert')
->findAdvertsByDate($advertType);
return $random;
}
}
5. Module: Advert\Module.php
public function getServiceConfig()
{
return array(
'invokables' => array(
'Advert\Service' => 'Advert\Service\LatestAdvertService',
),
);
}
public function getViewHelperConfig()
{
return array(
'factories' => array(
'latestAdvert' => 'Advert\View\Helper\LatestAdvertFactory',
),
);
}
As you can see in #4 I have a Variable called $advertType. I want to set the variable when I call the view Helper in my index.html, f.e. $this->latestAdvert('wanted'), but how can I pass this variable through all my files? I just can not find a solution for it. Does anyone got a tip for me how to achieve it? Thank you very much in advance.
!UPDATE!
As SenseException pointed out below, that injecting a service locator into a service is a bad practice and instead I should either inject repository or entity manager into the service, I have now worked out the first working solution for the entity manager.
For that I have updated 2 Files: module.php and LatestAdvertService.php
#5 module.php
public function getServiceConfig()
{
return array(
'factories' => array(
'Advert\Service' => function ($sl) {
$entityManager = $sl->get('doctrine.entitymanager.orm_default');
$myService = new Service\LatestAdvertService();
$myService->setEntityManager($entityManager);
//or you can set repository
//$repository = $entityManager->getRepository('Advert\Entity\Advert');
//$myService->setRepository($repository);
return $myService;
},
4. the Service: Advert\Service\LatestAdvertService .php
namespace Advert\Service;
use Advert\Entity\Advert as AdvertEntity;
use Doctrine\ORM\EntityManager;
class LatestAdvertService
{
public function setEntityManager(EntityManager $entityManager)
{
$this->entityManager = $entityManager;
}
public function setRepository(Repository $repository) {
$this->repository = $repository;
}
public function fetchSingleByAdvertType($advertType)
{
$random = $this->entityManager->getRepository('Advert\Entity\Advert')->findAdvertsByDate($advertType);
// $random = $this->repository->findAdvertsByDate($advertType);
return $random;
}
}
I have tried to inject the repository but get the following error message:
Argument 1 passed to Advert\Service\LatestAdvertService::setRepository() must be an instance of Advert\Service\AdvertRepository, instance of Advert\Repository\AdvertRepository given, called in
I will continue to find a solution for the repository injection and update when successful.
How about this solution:
namespace Advert\View\Helper;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class LatestAdvertFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
$locator = $serviceLocator->getServiceLocator();
$service = $locator->get('Advert\Service');
$helper = new LatestAdvert($service);
return $helper;
}
}
And of course the helper class:
namespace Advert\View\Helper;
use Zend\View\Helper\AbstractHelper;
class LatestAdvert extends AbstractHelper
{
protected $service;
public function __construct($service)
{
$this->service = $service;
}
public function __invoke($advertType)
{
$latestAdverts = $this->service->fetchSingleByAdvertType($advertType);
return $latestAdverts;
}
}
And for the service:
public function fetchSingleByAdvertType($advertType)
{
$entityManager = $this->getServiceLocator()
->get('doctrine.entitymanager.orm_default');
$random = $entityManager->getRepository('Advert\Entity\Advert')
->findAdvertsByDate($advertType);
return $random;
}
I tried to keep your code as close to your original as possible but please hear some suggestions about the service locator. It is a bad practice to inject a service locator into a service like you did in LatestAdvertService. Since you only need a repository for your service, just inject that one into it. If you need the entity manager in your service, inject it instead. Your unittests will thank you.
I have installed this package https://github.com/Intervention/image with composer. I have added
'IntImage' => 'Intervention\Image\Facades\Image'
to config/app under aliases
I get the following error and cant figure out what I am doing incorrectly I am sure it has something to do with namespacing /autoloading but app/acme is in the psr-o section of composer.json
'Argument 1 passed to
Acme\Services\Images\InterventionImageEditor::__construct() must be an
instance of IntImage, none given, called in
/var/www/app/ACme/Providers/ImageEditorServiceProvider.php on line 14
and defined' in
/var/www/app/Acme/Services/Images/InterventionImageEditor.php:11
I have the following directory structure
app
acme
Providers
ImageEditorServiceProvider.php
Services
Images
ImageEditorInterface.php
InterventionImageEditor.php
and the content of the files
ImageEditorServiceProvider.php
<?php namespace Acme\Providers;
use Illuminate\Support\ServiceProvider;
use Acme\Services\Images\InterventionImageEditor;
/**
*
*/
class ImageEditorServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->bind('Acme\Services\Images\ImageEditorInterface', function () {
return new InterventionImageEditor();
});
}
}
ImageEditorInterface.php
<?php namespace Acme\Services\Images;
interface ImageEditorInterface
{
public function hello();
}
InterventionImageEditor.php
<?php namespace Acme\Services\Images;
use IntImage;
/**
*
*/
class InterventionImageEditor implements ImageEditorInterface
{
protected $imageeditor;
public function __construct(IntImage $imageeditor)
{
$this->imageeditor = $imageeditor;
}
public function hello()
{
$hello = 'hello';
return $hello;
}
}
Can I
Use IntImage;
in this way because it is a facade or am I missing something?
edit to include solution;
changing the service provider to the following resolved the problem
<?php namespace Acme\Providers;
use Illuminate\Support\ServiceProvider;
use Acme\Services\Images\InterventionImageEditor;
use IntImage;
/**
*
*/
class ImageEditorServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->bind('Acme\Services\Images\ImageEditorInterface', function () {
$intimage = new IntImage;
return new InterventionImageEditor($intimage);
});
}
}
The error is coming from ImageEditorServiceProder.php:
$this->app->bind('Acme\Services\Images\ImageEditorInterface', function () {
return new InterventionImageEditor();
});
Here you are instantiating the InterventionImageEditor without any parameters. You InterventionImageEditor requires one parameter of type IntImage.
If there are places where you won't have IntImage when instantiating InterventionImageEditor then you need to update your InterventionImageEditor::__construct so that it accepts null(possibly).
function __construct(IntImage $imageeditor = null)
{
if (is_null($imageeditor)) {
// Construct a default imageeditor
// $imageeditor = new ...;
}
$this->imageeditor = $imageeditor;
}
i am not sure you can using IntImage because this file is Facades.
if you want to extending the intervention class. you should add Intervention\Image\Image to your ImageEditorServiceProvider.
use Intervention\Image\Image;
class ImageEditorServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->bind('Acme\Services\Images\ImageEditorInterface', function () {
return new InterventionImageEditor(new Image);
});
}
}