Symfony - How to transfrom these 2 functions in DRY way? - symfony

I got a service where I got 2 functions doing a part where the same Repository is called but with different queries and different arguments passed.
Basically, I have this first function:
public function function1($date, Entity1 $entity1)
{
$repos = $this->em->getRepository(EntityCommon::class)->findAllByXXX($entity1, $date);
...//Operations unique to this function
$results = new functionDRY(); //part common to both starting with the foreach on $repos
}
and this second function:
public function function2($date, Entity2 $entity2)
{
$repos = $this->em->getRepository(EntityCommon::class)->findAllByYYY($entity2, $date);
...//Operations unique to this function
$results = new functionDRY(); //part common to both starting with the foreach on $repos
}
I wanted to create a third function where I would use DRY logic since a big chunk of code would be similar except in the query needed in the repository:
public function functionDRY()
{
foreach ($repos as $repo) {// only operations common to both
$somethings = $repo->getSomething()->getValues();
foreach ($somethings as $something) {
}
}
}
How could I write the 3rd method since on I use foreach on $repos but it's defined in the 2 other functions?
Also, I tried to call the functionDRY in the 2 first functions but it doesn't work. I tried using directly functionDRY() but it didn't work either.
How can I call the function in both function1 and function2?
That might be rookie mistakes I would avoid in the future.

You may want to reverse your logic. The sample below would use the method AService::functionDRY for both entities and the method gets $repos from the appropriate function1 or function2 based on the class of the entity.
class AService
{
public function function1($date, Entity1 $entity1)
{
$repos = $this->em->getRepository(EntityCommon::class)->findAllByXXX($entity1, $date);
//Operations unique to this function
return $repos;
}
public function function2($date, Entity2 $entity2)
{
$repos = $this->em->getRepository(EntityCommon::class)->findAllByYYY($entity2, $date);
//Operations unique to this function
return $repos;
}
public function functionDRY($date, $entity)
{
$repos = [];
if (is_a($entity, Entity1::class)) {
$repos = $this->function1($date, $entity);
}
elseif (is_a($entity, Entity2::class)) {
$repos = $this->function2($date, $entity);
}
foreach ($repos as $repo) {// only operations common to both
$somethings = $repo->getSomething()->getValues();
foreach ($somethings as $something) {
}
}
}
}

Related

How to unit test createQueryBuilder

I have this method:
public function findAllBy(?string $categoryId): array
{
$qb = $this->entityManager->createQueryBuilder()
->select('p')
->from(Product::class, 'p');
if (null !== $categoryId) {
$qb->from(Category::class, 'cc')
->join(CategoryProduct::class, 'cp', Join::WITH, 'p.id = cp.product_id')
->join(Category::class, 'c', Join::WITH, 'cp.category_id = c.id')
->where('cc.id = :id')
->andWhere('cc.left <= c.left')
->andWhere('cc.right >= c.right')
->setParameter('id', $categoryId, 'uuid');
}
return $qb->getQuery()->getResult();
}
I am trying to test it this way ( obviously not the correct one ):
public function testFindAllProductsByFilters():void
{
$entityRepositoryMock = $this->createMock(EntityRepository::class);
$entityManagerMock = $this->createMock(EntityManagerInterface::class);
$entityManagerMock->expects($this->once())
->method('getRepository')
->with(Product::class)
->willReturn($entityRepositoryMock);
$entityManagerMock->expects($this->once())
->method('createQueryBuilder')
->willReturn($entityRepositoryMock);
$this->repository = new DoctrineProductRepository($entityManagerMock);
$this->assertIsArray($this->repository->findAllBy(ProductFactory::CATEGORY_ID_FIRST));
}
The error I get:
1)
App\Tests\Unit\Infrastructure\Domain\Model\Product\DoctrineProductRepositoryTest::testFindAllProductsByFilters
Error: Call to a member function from() on null
Is this piece of code even testable by Unit Test ?
As you should not mock what you don't own, my suggestion is to avoid unit tests in this kind of scenarios.
Moreover I would not use (abuse) a mock (a test double in general) as you're testing the implementation and not the behaviour of your SUT.
Let's see an example
class Foo()
{
public function doSomething(): int
{
// some heavy logic here
}
}
class Bar()
{
public function doSomething(Foo $foo): int
{
$result = $foo->doSomething();
// other elaboration upon $result
}
}
That's a deliberately trivial example of course. If you use a test double in Bar test, you would write something like`
$fooMock->expects($this->once())
->method('doSomething')
->willReturn(1);
Let's say that Foo changes it's public API, renaming doSomething to doSomethingElse. What happens? You need to change the test aswell even if Foo behaviour didn't changed at all.
As said before it's a trivial example but should give you an idea.

Symfony call get by Name from variable

I would like to call a getter with the stored fieldname from the database.
For example, there are some fieldnames store like ['id','email','name'].
$array=Array('id','email','name');
Normally, I will call ->getId() or ->getEmail()....
In this case, I have no chance to handle things like this. Is there any possibility to get the variable as part of the get Command like...
foreach ($array as $item){
$value[]=$repository->get$item();
}
Can I use the magic Method in someway? this is a bit confusing....
Symfony offers a special PropertyAccessor you could use:
use Symfony\Component\PropertyAccess\PropertyAccess;
$accessor = PropertyAccess::createPropertyAccessor();
class Person
{
private $firstName = 'Wouter';
public function getFirstName()
{
return $this->firstName;
}
}
$person = new Person();
var_dump($accessor->getValue($person, 'first_name')); // 'Wouter'
http://symfony.com/doc/current/components/property_access/introduction.html#using-getters
You can do it like this :
// For example, to get getId()
$reflectionMethod = new ReflectionMethod('AppBundle\Entity\YourEntity','get'.$soft[0]);
$i[] = $reflectionMethod->invoke($yourObject);
With $yourObject being the object of which you want to get the id from.
EDIT : Don't forget the use to add :
use ReflectionMethod;
Hope this helps.
<?php
// You can get Getter method like this
use Doctrine\Common\Inflector\Inflector;
$array = ['id', 'email', 'name'];
$value = [];
foreach ($array as $item){
$method = Inflector::classify('get_'.$item);
// Call it
if (method_exists($repository, $method))
$value[] = $repository->$method();
}

codeigniter recursive model function returning blank but when printing it in the model showing properly

codeigniter recursive model function returning blank but when printing it in the model showing properly
here is my code,
for controller
$commision_arr=$this->billing_model->root_commision($category_manager['id']);
and in the model
public function root_commision($id)
{
$sql="SELECT * FROM tbl_mst_category WHERE id = '".$id."'";
$query = $this->db->query($sql);
$row=$query->row_array();
if($row['parent']!=0)
{
$this->root_commision($row['parent']);
}
else
return $row;
}
recursion is tricky huh?
i think the problem is that you were only returning the id for the deepest element, but not returning that to the calling method -- so it would only work for the case where the parent id was called. i can't test the code below, but it should point you in the right direction. NB, it returns the row as an object, not as an array as your code does.
On a more academic note, if the table is large it may be better to pre-calculate these root ids for each of these categories. it will make the query much faster -- recursion is not fast. look at transitive closures
public function root_commision($id,$root_found = FALSE)
{
// returns FALSE if the id is not found, or the parent row.
$query = $this->db->get_where('tbl_mst_category', array('id' => $id));
if ($query->num_rows() > 0 )
{
$row = $query->first_row();
if (($row->parent ) != 0 )
{
return $this->root_commision($row_id);
}
else
{
return $row;
}
}
else
{
return FALSE;
}
}
you have to return function at the calling time then only you can get
the value of recursive function just add "return" keyword before function call.
public function root_commision($id)
{
$sql="SELECT * FROM tbl_mst_category WHERE id = '".$id."'";
$query = $this->db->query($sql);
$row=$query->row_array();
if($row['parent']!=0)
{
return $this->root_commision($row['parent']);
}
else
return $row;
}

Laravel 4 Model Events don't work with PHPUnit

I build a model side validation in Laravel 4 with the creating Model Event :
class User extends Eloquent {
public function isValid()
{
return Validator::make($this->toArray(), array('name' => 'required'))->passes();
}
public static function boot()
{
parent::boot();
static::creating(function($user)
{
echo "Hello";
if (!$user->isValid()) return false;
});
}
}
It works well but I have issues with PHPUnit. The two following tests are exactly the same but juste the first one pass :
class UserTest extends TestCase {
public function testSaveUserWithoutName()
{
$count = User::all()->count();
$user = new User;
$saving = $user->save();
assertFalse($saving); // pass
assertEquals($count, User::all()->count()); // pass
}
public function testSaveUserWithoutNameBis()
{
$count = User::all()->count();
$user = new User;
$saving = $user->save();
assertFalse($saving); // fail
assertEquals($count, User::all()->count()); // fail, the user is created
}
}
If I try to create a user twice in the same test, it works, but it's like if the binding event is present only in the first test of my test class. The echo "Hello"; is printed only one time, during the first test execution.
I simplify the case for my question but you can see the problem : I can't test several validation rules in different unit tests. I try almost everything since hours but I'm near to jump out the windows now ! Any idea ?
The issue is well documented in Github. See comments above that explains it further.
I've modified one of the 'solutions' in Github to automatically reset all model events during the tests. Add the following to your TestCase.php file.
app/tests/TestCase.php
public function setUp()
{
parent::setUp();
$this->resetEvents();
}
private function resetEvents()
{
// Get all models in the Model directory
$pathToModels = '/app/models'; // <- Change this to your model directory
$files = File::files($pathToModels);
// Remove the directory name and the .php from the filename
$files = str_replace($pathToModels.'/', '', $files);
$files = str_replace('.php', '', $files);
// Remove "BaseModel" as we dont want to boot that moodel
if(($key = array_search('BaseModel', $files)) !== false) {
unset($files[$key]);
}
// Reset each model event listeners.
foreach ($files as $model) {
// Flush any existing listeners.
call_user_func(array($model, 'flushEventListeners'));
// Reregister them.
call_user_func(array($model, 'boot'));
}
}
I have my models in subdirectories so I edited #TheShiftExchange code a bit
//Get all models in the Model directory
$pathToModels = '/path/to/app/models';
$files = File::allFiles($pathToModels);
foreach ($files as $file) {
$fileName = $file->getFileName();
if (!ends_with($fileName, 'Search.php') && !starts_with($fileName, 'Base')) {
$model = str_replace('.php', '', $fileName);
// Flush any existing listeners.
call_user_func(array($model, 'flushEventListeners'));
// Re-register them.
call_user_func(array($model, 'boot'));
}
}

Symfony2 set class variable with init or construct methods

Have recently been using Symfony2 after using ZF for some time.
I am having problems trying to do something relatively simple, I think.
The following code is within a controller:
private $current_setid = "";
public function __construct() {
$current_set = $this->getCurrentSet();
if ($current_set == "") {
return $this->redirect($this->generateUrl('selectset'));
}
$this->current_setid = $current_set;
}
public function getCurrentSet() {
$session = $this->get("session");
$set = $session->get('set');
return $set;
}
public function setCurrentSet($setid) {
$session = $this->get("session");
$session->set('set', "$setid");
}
If I use __construct() I get errors like:
Fatal error: Call to a member function get() on a non-object in
I have tried using __init() and init() both of which do not seem to get called.
Can anyone point me in the right direction? Is there a simple way to do this or do I have to look into event listeners?
Have you tried getting your session like they do in official documentation?
$session = $this->getRequest()->getSession();
$foo = $session->get('foo');
Basically get fetch dependencies from container and container in the Controller is injected using setter dependency injection. You just not have container in the time of __construct yet.
Just ended up opting for placing a check in every method in the class. Seems silly to have to do that but I find I often have to do that in Symfony2 with the lack of init, postDispatch type methods like ZF has.
Even trying to remove the check to another method was counter productive as I still had to check the return from that method as $this->redirect does not seem to work unless it is within an Action method. For example:
public function isSetSet() {
$current_set = $this->getCurrentSet();
if ($current_set == "") {
$url = $this->generateUrl('selectset');
return $this->redirect($url);
}
return TRUE;
}
public function someAction() {
$check = $this->isSetSet();
if($check != TRUE){
return $check;
}
...
}
So each method needs that 4 line check but the whole check can be done in 4 lines anyway so no need for that extra method:
public function anotherAction() {
$current_setid = $this->getCurrentSet();
if ($current_setid == "") {
return $this->redirect($this->generateUrl('selectset'));
}
...
}

Resources