How to test generated id in aggregate root - php

I wanna to test my entities created from doctrine, but i don`t know how to test id if they are generated themself while object save to database.
class Dish
{
private ?int $id;
private Collection $prices;
public function __construct()
{
$this->prices = new ArrayCollection();
}
public function identifier(): string
{
return $this->id;
}
public function isEquals(Dish $dish): bool
{
return $this->identifier() === $dish->identifier();
}
public function addPrice(float $price)
{
$this->prices->add(new DishPrice($this, $price));
}
}
class DishPrice
{
use DeletedEntityTrait;
private ?int $id;
private float $price;
private Dish $dish;
public function __construct(Dish $dish, float $price)
{
$this->dish = $dish;
$this->price = $price;
}
public function identifier(): string
{
return $this->id;
}
public function isEquals(Dish $dish): bool
{
return $this->identifier() === $dish->identifier();
}
}
How to test isEquals methods in both classes?

Basically you have two options.
First option is functional test. You can create two entities and persist them to the database.
Second option is pass ID to the constructor. To be able to do this you can add method to repository that will provide you next ID for your entity in service layer:
interface DishRepository
{
public function getNextIdentity(): int;
}
$id = $repository->getNextIdentity();
$dish = new Dish($id);
and in unit-tests you can pass to the constructor whatever ID that you want
$testId = 111;
$dish = new Dish($testId);

Try to use uuid instead of int and testing will be easer) also, you will have more benefits while using uuids.

Related

Strategy for updating huge entity's collection that belongs to Aggregate Root

I'm stuck with a specific scenario regarding Aggregates and not breaking business invariants.
I've two entities, let's call them Order and OrderItem. Both entities belong to an aggregate with Order as Aggregate root.
When I need to update one specific OrderItem I do it through the Aggregate root:
class Order
{
private Collection $orderItems;
public function __construct()
{
$this->orderItems = new Collection;
}
public function updateOrderItemPrice(OrderItemId $id, int $newPrice): void
{
foreach ($this->orderItems as $orderItem) {
if ($orderItem->id()->equals($id) {
$orderItem->updatePrice($newPrice);
break;
}
}
}
}
While this solution works fine for small collections, it can lead to a huge performance penalty when we're talking about thousands (or tens of thousands) of records due the ORM (Doctrine in my case) will try to load all the order items in memory when you're about to iterate it.
Is there any pattern or good practice to solve this specific scenario?
class OrderItemId {
public function __construct(private readonly int $value)
{
}
public function getValue(): int
{
return $this->value;
}
}
class OrderItem {
public function __construct(private readonly OrderItemId $id)
{
}
public function getId(): OrderItemId
{
return $this->id;
}
}
class OrderItemCollection {
private array $items = [];
private function add(OrderItem $item): void {
$this->items[$item->getId()->getValue()] = $item;
}
public function has(OrderItemId $id): bool
{
return array_key_exists($id->getValue(),$this->items);
}
public function get(OrderItemId $id): OrderItem
{
return $this->items[$id->getValue()];
}
}
class Order
{
private OrderItemCollection $orderItems;
public function __construct()
{
$this->orderItems = new OrderItemCollection;
}
public function updateOrderItemPrice(OrderItemId $id, int $newPrice): void
{
if($this->orderItems->has($id))
{
$this->orderItems->get($id)->updatePrice($newPrice);
}
}
}

How to pass an object to a view in Laravel?

I want to pass an object of Student Model to a view in Laravel. I tried using
return view('student/main')->with($student);
Where $student is a instance of Student Model (Code is below)
But it gave an error "Illegal offset type"
I know that data can be passed as an array to a view. But I really want to pass it as a object if possible. Then I can display data as follows by fetching data from get methods.
<h4 class="text-left"><strong>{{$student->getName()}}</strong> </h4>
I am looking for a solution which can be done using keeping objects instead of arrays.(if possible)
The Student model code is as follows. It consists with simply setters and getters.
class Student extends Model{
//attributes
private $student_id;
private $first_name;
private $last_name;
private $batch_id;
// set attributes
public function setID($student_id)
{
$this->student_id = $student_id;
}
public function setFirstName($first_name)
{
$this->first_name = $first_name;
}
public function setLastName($last_name)
{
$this->last_name = $last_name;
}
public function setBatchID($batch_id)
{
$this->batch_id = $batch_id;
}
// get attributes
public function getName()
{
return $this->first_name." ".$this->last_name;
}
public function getID()
{
return $this->student_id;
}
public function getBatchID()
{
return $this->batch_id;
}
You've got a number of options to do that
return view('student/main', ['student'=> $student]);
return view('student/main', compact('student'));
return view('student/main')->with('student', $student);
return view('student/main')->withStudent($student);
You have to name your variable:
return view('student/main')->with(['student' => $student]);

Custom userIdentity class in yii2

I want to create custom userIdentity class according to my specific requirements .Here the code is
<?php
namespace app\models;
use yii\web\IdentityInterface;
use app\models\dbTables\Users;
class UserIdentity implements IdentityInterface{
const ERROR_USERNAME_INVALID=3;
const ERROR_PASSWORD_INVALID=4;
const ERROR_NONE=0;
public $errorCode;
private $_id;
private $_email;
private $_role;
private $_name;
public function findIdentityById($id){
$objUserMdl = new Users;
$user = $objUserMdl::findOne($id);
$userRole = $objUserMdl->getUserRole($user->user_id);
$this->_id = $user->user_id;
$this->_email = $user->email_address;
$this->_role = $userRole;
$this->_name = $user->full_name;
return $this;
}
public function getId()
{
return $this->_id;
}
public function getName(){
return $this->_name;
}
public function getEmail(){
return $this->_email;
}
public function getRole(){
return $this->_role;
}
public static function findIdentity($id)
{
return self::findIdentityById($id);
}
public function getAuthKey()
{
throw new NotSupportedException('"getAuthKey" is not implemented.');
}
public function validateAuthKey($authKey)
{
throw new NotSupportedException('"validateAuthKey" is not implemented.');
}
public static function findIdentityByAccessToken($token, $type = null)
{
throw new NotSupportedException('"findIdentityByAccessToken" is not implemented.');
}
}
?>
Basically I have two tables roles and users and I want to set the specific properties from both table in yii::$app->user->identity
When I call the above code the findIdentity($id) function returns error for obvious reasons stating that I cannt call $this in static funtion . How can I set the required properties in function and return the instance of userIdentity class from it ?
I recommend reading this: When to use self over $this? you are really confusing the 2.
$objUserMdl = new Users;
$user = $objUserMdl::findOne($id);
$userRole = $objUserMdl->getUserRole($user->user_id);
You are calling :: on an object, you cannot do that.
I say delete what you have done and start again, it should be much easier then what you wrote. It would take a long time to show you how to do it properly, just look in the yii2 advance template and see how they are doing it. You can use your own identity class and set up any special attributes there. Just study the yii2 code.

PHP ORM how to set id in constructor

I have a basic problem with following code:
<?php
interface UserInterface
{
public function getId();
public function getName();
}
class User implements UserInterface
{
private $_id;
private $_name;
public function __construct($id, $name)
{
$this->_id = $id;
$this->_name = $name;
}
public function getId()
{
return $this->_id;
}
public function getName()
{
return $this->_name;
}
}
class UserMapper
{
public function insert(UserInterface $user)
{
... insertion code
}
}
?>
The insert method of the UserMapper expects an UserInterface object. So I create one:
<?php
$user = new User(1, "Chris");
$userMapper = new UserMapper();
$userMapper->insert($user);
?>
My problem is, that the user's id is an auto-increment value that is coming from the database after inserting the object. But the object's constructor forces me to define an id because the object would not be complete without an id. How to solve that general problem?
To pass the id as a second parameter to the constructor woth a default value is not an option, because in my understanding the object would be incomplete without having an id.
Thanks for your help.
You can pass null:
$user = new User(null, "Chris");
$_id is not checked for a valid integer and with null you know that this model has no valid ID yet.

How this class and sub methods use works?

I have been browsing some php source code and need to know how the following class and sub methods use works:
<?php
$me = new Person;
$me->name("Franky")->surname("Chanyau")->phone("+22", "456 789");
?>
I have pretty solid knowledge of OOP so I don't want a 101. I just need to know how to make the above code possible.
Method chaining is possible, by
return $this;
at the end of the method.
Explained here:
phpandstuff: Method Chaining Plus Magic Setters
These methods usually set an instance variable and then just return $this.
public function phone($param) {
$this->phone = $param;
return $this;
}
methods name() surname() and phone() return an instance of Person. you can accomplish this by
return $this;
most probably these methods look like this:
public function name($name) {
$this->name = $name;
return $this;
}
like some others said, its a fluid interface http://en.wikipedia.org/wiki/Fluent_interface#PHP the Basic Idea is that a methof of a class always returns the object itself
class Car {
private $speed;
private $color;
private $doors;
public function setSpeed($speed){
$this->speed = $speed;
return $this;
}
public function setColor($color) {
$this->color = $color;
return $this;
}
public function setDoors($doors) {
$this->doors = $doors;
return $this;
}
}
// Fluent interface
$myCar = new Car();
$myCar->setSpeed(100)->setColor('blue')->setDoors(5);
(via wiki)
It's called method chaining. Basically each class function returns the object itself ($this) so that the user can call more functions on the returned object.
public function name() {
//other stuff...
return $this;
}
http://www.talkphp.com/advanced-php-programming/1163-php5-method-chaining.html
http://www.electrictoolbox.com/php-method-chaining
The idea is if we return $this then we can chain the object method calls together. Here's the solution:
<?php
class Person
{
private $strName;
private $strSurname;
private $ArrPhone = array();
public function name($strName)
{
$this->strName = $strName;
return $this; // returns $this i.e Person
}
public function surname($strSurname)
{
$this->strSurname = $strSurname;
return $this; // returns $this i.e Person
}
public function phone()
{ $this->ArrPhone = func_get_args(); //get arguments as array
return $this; // returns $this i.e Person
}
public function __toString()
{
return $this->strName." ".$this->strSurname.", ".implode(" ",$this->ArrPhone);
}
}
$me = new Person;
echo $me->name("Franky")->surname("Chanyau")->phone("+22", "456 789");
?>
Correct answers, but to make the code work you should write:
$me = new Person();
instead of
$me = new Person;

Categories