Moving entities across collections in Doctrine - php

This question is a bit related, to my other question here
ArrayCollection ( Doctrine ) contains function returns incorrect results , so if anyone wants more information about my problem, it is there, although this is not strictly the same problem.
Now, to the point, I have a FileSystemFolder that models a folder, and a FileProxy that models a File, FileSystemFolder contains an ArrayCollection of FileProxy, I also have a FileManager, that performs a move operation, something that you would expect to have on any FileSystem. This move operation takes two FileSystemFolder and a FileProxy, removes this FileProxy from one FileSystemFolder and adds it into another FileSystemFolder.
Here is this function, in all its glory:
public function moveFileProxy(FileSystemFolder $from, FileSystemFolder $to, FileProxy $proxy, $force = false)
{
if (!$this->checkFolder($from))
{
return array('type' => 'error', 'message' => 'Cannot move from this folder.');
}
if (!$force)
{
if (!$this->checkFolder($to))
{
return array('type' => 'error', 'message' => 'Cannot move to this folder.');
}
}
/*$return = "";
foreach($from->getFiles() as $file)
{
$return .= $file->getFilename() . " --- ";
}
if(!$from->getFiles()->contains($proxy))
{
return array('type' => 'error', 'message' => 'Folder '.$from->getName().' does not contain '.$proxy->getFilename(). ' All files from this folder '. $return);
}
if($to->getFiles()->contains($proxy))
{
return array('type' => 'error', 'message' => 'Folder '.$to->getName().' already contains '.$proxy->getFilename());
}*/
$from->removeFile($proxy);
$to->addFile($proxy);
return array('type' => 'pass', 'message' => 'Operation Successful.');
}
The commented out piece of code belongs to the other question. Checks do not matter.
For the sake of this question, I'll copy one piece of information from that other question, because it is important, here it is:
/**
* #ORM\ManyToMany(targetEntity="FileProxy", fetch="EXTRA_LAZY")
* #ORM\JoinTable(name="file_system_folders_files",
* joinColumns={#ORM\JoinColumn(name="file_system_folder_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="proxy_id", referencedColumnName="id", unique=true)})
*/
protected $files;
and this:
/**
* Add proxy
*
* #param FileProxy $proxy
* #return FileSystemFolder
*/
public function addFile(FileProxy $proxy)
{
if(!$this->getFiles()->contains($proxy))
{
$this->getFiles()->add($proxy);
}
return $this;
}
/**
* Remove proxy
*
* #param FileProxy $proxy
* #return FileSystemFolder
*/
public function removeFile(FileProxy $proxy)
{
if($this->getFiles()->contains($proxy))
{
$this->getFiles()->removeElement($proxy);
}
return $this;
}
Now, these functions are exteremely straightforward, they really do not do anything fancy, exactly what their names tell, and the funny thing is,
the moveFileProxy function works perfectly fine ( I persist both folders out of this function, which I think is a good practice ).
The question is: Should this function actually work ? As you can see, the $files variable, which is an ArrayCollection does not cascade persist, it doesn't cascade anything, yet this function still works.
Here is a piece of code showing the whole process:
$result = $fm->moveFileProxy($origin, $destination, $upload);
if($result['type'] === 'error')
{
return JsonResponse::create($result);
}
$em = $this->getDoctrine()->getManager();
$em->persist($origin);
$em->persist($destination);
$em->flush();
But it works just most of the time, it does not throw errors most of the time, but sometimes, on some weird occassions, it throws "Duplicate entry exception" which is also weird, because this is after all a move operation.
If you know anything about this problem, please help me, #symfony and #doctrine IRCs are really awful at helping.
Thanks.

It would seem, that I discovered what might be the answer to this question ( completely by accident ), bare in mind, that I am not entirely sure, and I do not think it is documented in Doctrine Documentation. It might be buried somewhere in the code, and I might look that up later to prove this theory.
My theory is:
Unidirectional Many-To-Many associations create an additional table, so that the association is composed of 3 tables, with no actual Many-To-Many association, the 2 tables are obviously the ones you used to form the association, but the third one is important.
The third table is created by Doctrine on the spot, and even though we specify the properties for the columns with #JoinColumn, I think Doctrine actually modyfies them further to cascade persist, because it would make no real sense otherwise.
I am also pretty sure, that it actually cascades all, but like I said, it's just a theory.

Related

command to handler to aggregate root to repository flow in DDD and CQRS

while learning DDD and cqrs, here is something I need to clarify. In a shopping context, I have a customer which I believe is my aggregate root and I would like to implement the simple use-case of change customer name.
Here is my implementation take on this using DDD/CQRS as much as I know.
My concerns are
for validation, should the command also validate the input to make it conform with the value object or is it okay to leave it to handler?
is my overall flow alright or am I heavily missing somewhere?
If this flow is right, I see that Customer Aggregate root will be a huge class with numerous functions like changeName, changeAddress, changePhoneNumber, deleteSavedPaymentMethod and so on.
It will become a god class, and that seems a bit odd to me, is it actually the right way of DDD aggregate root implementation?
// Value Object
class CustomerName
{
private string $name;
public function __construct(string $name)
{
if(empty($name)){
throw new InvalidNameException();
}
$this->name = $name;
}
}
// Aggregate Root
class Customer
{
private UUID $id;
private CustomerName $name;
public function __construct(UUID $id, CustomerName $name)
{
$this->id = $id;
$this->name = $name;
}
public function changeName(CustomerName $oldName, CustomerName $newName) {
if($oldName !== $this->name){
throw new InconsistencyException('Probably name was already changed');
}
$this->name = $newName;
}
}
// the command
class ChangeNameCommand
{
private string $id;
private string $oldName;
private string $newName;
public function __construct(string $id, string $oldName, string $newName)
{
if(empty($id)){ // only check for non empty string
throw new InvalidIDException();
}
$this->id = $id;
$this->oldName = $oldName;
$this->newName = $newName;
}
public function getNewName(): string
{
return $this->newName; // alternately I could return new CustomerName($this->newName)] ?
}
public function getOldName(): string
{
return $this->oldName;
}
public function getID(): string
{
return $this->id;
}
}
//the handler
class ChangeNameHandler
{
private EventBus $eBus;
public function __construct(EventBus $bus)
{
$this->eBus = $bus;
}
public function handle(ChangeNameCommand $nameCommand) {
try{
// value objects for verification
$newName = new CustomerName($nameCommand->getNewName());
$oldName = new CustomerName($nameCommand->getOldName());
$customerTable = new CustomerTable();
$customerRepo = new CustomerRepo($customerTable);
$id = new UUID($nameCommand->id());
$customer = $customerRepo->find($id);
$customer->changeName($oldName, $newName);
$customerRepo->add($customer);
$event = new CustomerNameChanged($id);
$this->eBus->dispatch($event);
} catch (Exception $e) {
$event = new CustomerNameChangFailed($nameCommand, $e);
$this->eBus->dispatch($event);
}
}
}
//controller
class Controller
{
public function change($request)
{
$cmd = new ChangeNameCommand($request->id, $request->old_name, $request->new_name);
$eventBus = new EventBus();
$handler = new ChangeNameHandler($eventBus);
$handler->handle($cmd);
}
}
PS. some classes like UUID, Repo etc skipped for brevity.
should the command also validate the input to make it conform with the value object or is it okay to leave it to handler?
"Is it okay" -- of course; the DDD police are not going to come after you.
That said, you may be better off in the long run designing your code so that the different concepts are explicit, rather than implicit.
For example:
$cmd = new ChangeNameCommand($request->id, $request->old_name, $request->new_name);
What this tells me -- a newcomer to your code base -- is that ChangeNameCommand is an in memory representation of the schema of your HTTP API, which is to say it is a representation of your contract with your consumers. Customer contracts and domain models don't change for the same reasons, so it may be wise to make the separation of the two explicit in your code (even though the underlying information is "the same").
Validation that the values that appear in the http request really do satisfy the requirements of the customer schema should happen out near the controller, rather than in near the model. It's the controller, after all, that is responsible for returning client errors if the payload doesn't satisfy the schema (ex: 422 Unprocessable Entity).
Having verified that the input is satisfactory, you can then transform the information (if necessary) from the HTTP representation of the information to the domain model's representation. That should always Just Work[tm] -- if it doesn't it indicates that you have a requirements gap somewhere.
It doesn't particularly matter where this translation happens; but if you were to imagine having multiple different schemas, or different interfaces that accept this information (a command line app, or a queue reading service, or something), then the translation code probably belongs with the interface, rather than with the domain model.
is my overall flow alright or am I heavily missing somewhere?
Your composition choices look suspicious - in particular the fact that the lifetime of the EventBus belongs to Controller::change but the lifetime of the CustomerRepo belongs to ChangeNameHander::handle.
It will become a god class...
Then break it up. See Mauro Servienti's 2019 talk.
Truth is: data models that are just storing copies of information provided by the outside world aren't particularly interesting. The good bits, that really justify the investment of work, are the state machines that decide things based on the information provided by the outside world.
If a state machine doesn't use a piece of information to make decisions, then that information belongs "somewhere else" - either a different state machine, or someplace less complicated like a database or a cache.

Laravel response transformations not working

I have the following
controller function
public function show()
{
$users_id = Request::segment(4);
// $this->user_bank_details_repository->setPresenter(new UserBankAccountPresenter);
$account = $this->user_bank_details_repository->findByField('users_id', $users_id, $columns = ['user_bank_details_id','bank_name','bank_account_number','bank_ifsc_code','beneficiary_name','bank_account_type','bank_branch','created_at']);
if(!$account)
{
return $this->response->noContent();
}
return $this->response->item($account, new UserBankAccountTransformer);
}
Transformer
<?php
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
namespace App\Api\V1\Transformers;
use App\Entities\UserBankDetails;
use League\Fractal\TransformerAbstract;
class UserBankAccountTransformer extends TransformerAbstract {
public function transform(UserBankDetails $bank_details)
{
return [
'id'=> (int) $bank_details->user_bank_details_id,
'bank_name' => $bank_details->bank_name,
'bank_account_number' => (int) $bank_details->bank_account_number,
'bank_account_type' => $bank_details->bank_account_type,
'bank_beneficiary_name' => $bank_details->beneficiary_name,
'bank_branch'=> $bank_details->bank_branch
];
}
}
I am using repository pattern design and using dingo for my REST framework.
I always get the response as
{
"user_bank_details": [
{
"user_bank_details_id": 1,
"bank_name": "jbjb",
"bank_account_number": "939393933939",
"bank_ifsc_code": "ABCD0000047",
"beneficiary_name": "Gaf",
"bank_account_type": "savings",
"bank_branch": "Mad(E)",
"created_at": "2015-12-23 17:05:39"
}
]
}
instead of the transformed json. I dont get any errors as well. Tried using
return $this->response->item($account, new UserBankAccountTransformer::class);
But still the response is not getting transformed. I tried whatever I could but I dont get it worked :(
Even though this question is over a year ago, however for the sake of anyone who would have similar issue, the suspect here is the fact that you used: item instead of collection.
If the $account contains a collection, then it means this answer is correct: that is, this:
return $this->response->item($account, new UserBankAccountTransformer::class);
Should be:
return $this->response->collection($account, new UserBankAccountTransformer::class);
PS: I also Encountered this on Laravel 5.2.* thats why I feel this might help.

doctrine 2 cascade persist saving too much

so this is my prePersist on EventListener
public function prePersist(LifecycleEventArgs $args)
{
//the first entity will have the PMP, so we catch it and continue to skip this if after this
if ($this->pmp == null) {
$this->pmp = $args->getEntity()->getPmp();
}
$taxonomicClass = $args->getEntity();
if($taxonomicClass instanceof TaxonomicClass){
if(is_null($taxonomicClass->getId())){
//here it says that i have created a new entity, need to persist it via cascade={"persist"}
$taxonomicClass->setPmp($this->pmp);
}
}
}
that's fine, i had added the annotation on it:
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Pmp", cascade={"persist"})
* #ORM\JoinColumn(name="pmp_id", referencedColumnName="id", nullable=false)
**/
private $pmp;
and it saves everything from my hierarchy, even a new PMP, an object that already exist in the database!
what i want is that everything that im saving from my hierarchy needs to be related to the PMP that i passed, but when i set $taxonomicClass->setPmp($this->pmp); doctrine thinks that i created a new instance of PMP, since im not, i just want to this guy have an associaton with the PMP.
i tried put merge on the cascade option, but it only works with persist, how to make doctrine dont create a new instance, and instead use the one that i passed?
noticed my problem, i was assigning an attribute from memory, i should retrive him from database to doctrine understand.
public function prePersist(LifecycleEventArgs $args)
{
if ($this->pmp == null) {
$this->pmp = $args->getEntity()->getPmp();
}
$taxonomicClass = $args->getEntity();
if($taxonomicClass instanceof TaxonomicClass){
if(is_null($taxonomicClass->getId())){
//this solved the problem
$pmp = $args->getEntityManager()->getRepository("AppBundle:Pmp")->find($this->pmp->getId());
$taxonomicClass->setPmp($pmp);
}
}
}
i will keep in mind now that when a new entity is created, but it doesn't need to be saved, you must retrieve it from db, cascade={"persist"} wasn't even necessary

Behat - Using an example table String in an xpath selector array

I have been using Behat for a year or so at a level fine for the automation of most websites but I now need to start using it more for user generated content, I am relatively new to PHP and at the moment I am struggling how to use a String entered in an Example table in an x-path array:
Feature: Campaign
Scenario Outline: Pass campaign string to xpath array
Then I add a new campaign name of "<campaign>"
Examples:
|campaign |
|Automation|
The context file looks like this
/**
* #Then /^I add a new campaign name of "([^"]*)"$/
*/
public function iAddANewCampaignNameOf($campaign)
{
/**
* #var CreateCampaign $createCampaign
*/
$createCampaign= $this->getPage('CreateCampaign');
$createCampaign->campaignName($campaign);
}
Then I use the Page Object extension for the class Campaign.php
class CreateCampaign extends AutomationPage
{
protected $path = 'someURL';
public $campaign;
protected $elements = array(
'campaignHeader' => array('xpath' => "//*[#id='site-navigation-campaigns']"),
);
public function campaignName ($campaign)
{
$this->campaign = $campaign;
$this->getSession()->wait(5000);
$this->getElement('campaignName')->setValue($campaign);
}
So far so good, the tester can enter a campaign name of "Automation" - it gets passed through the context file and the campaign name is set in the browser.
What I am lacking is to be able to retain this $campaign name string and use it in another page so I can reference it in another array i.e. for selecting an existing campaign as follows:
SecondPageObjectPage.php
class ReferenceCampaign extends AutomationPage
{
protected $path = 'someURL';
protected $elements = array(
'referenceCampaign' => array('xpath' => "//*[contains(#id,'***HERE I NEED TO GET THE
$campaign value"),
);
public function editExistingCampaign ($campaign)
{
$this->getElement('referenceCampaign')->click();
}
}
I have tried my best to simplify things and I can explain further if any of this isnt clear - hopefully its just a simple PHP question and not really Behat specific
Thanks Ian
Your example is a much better way of doing things, I have only recently started using partial contains and it expands the flexibility of finding stubborn xpaths especially if you combine more than one, like the working example below:
public function editExistingCampaign ($campaign)
{
$this->getSession()->wait(5000);
$element = $this->find('xpath', '//*[contains(#id,"'.$campaign.'")]
[contains(#id,"actionbuttons")]');
if (isset($element)) {
$element->click();
} else {
throw new Exception('Element not found');
}
}
The only slight change was to add a ] at the end of the x-path
I'm sure it's a simple question, but I think that I am missing a point. If all you want is to get hold of the value that was used on the page then you need to review your code structure. First, you cannot pass method argument to the property definition in another class, but you can find the element inside editExistingCampaign.
class ReferenceCampaign extends AutomationPage
{
protected $path = 'someURL';
public function editExistingCampaign ($campaign)
{
$element = $this->find('xpath', '//*[contains(#id, "' . $campaign . '")]');
if (isset($element)) {
$element->click();
} else {
throw new Exception('Element not found');
}
}
}
I'm assuming you are using Symfony Page Object extension, which you should mention. I'm not sure if I've got the syntax right, but the idea is to find your element inside the method.

Logging design pattern in a Zend framework application

I'm building an application using Zend Framework. The application requires intensive logging for every action or function in the code.
so my code most of the time looks like this:
function SendMailsAction(){
$logger->log('Started sending mails.')
...
...
...Some Code...
...
...
foreach ($mails as $mail){
try{
$logger->log('Trying to send')
$mail->send()
$logger->log('Mail sent successfully.')
}catch(Exception $e){
$logger->log('Failed to send mail.')
}
}
...
...
...Some Code...
...
...
$logger->log('Finished sending mails.')
}
Sometimes I even have to log in 2 tables, so most of the logging code is doubled and the functions start to get complicated and long.
I use Zend framework's Zend_Log for logging so my problem is not the logging class itself, But it's how to separate the logging code from the code functionality itself and maintain separation of concerns.
Some people suggested Aspect Oriented Programming (AOP), but unfortunately AOP for PHP isn't acceptable for my costumer, so I'm looking for an Object Oriented solution or best practice.
Note:
Just to make things clear my problem isn't how to use Zend_Log, but how to add logging to my application code in general.
Sometimes I even have to log in 2 tables, so most of the logging code is doubled and the functions start to get complicated and long.
It'll be long. If your code does a lot of logging, it will be long, as it will have to log, and each line action it logs, will mean there's a line in your code. It shouldn't, however, be complicated in any case as logging is one of the most straightforward thing you can do. What worries me, is that you mention "sometimes I even have to log in 2 tables". In my book, one, two, five, sixty or one thousand tables is performed by one line. Code is not doubled for each logger. If you're copy-pasting a line, and changing $log to $log2, you're clearly doing it wrong (tm).
Some people suggested Aspect Oriented Programming (AOP), but unfortunately AOP for PHP isn't acceptable for my costumer, so I'm looking for an Object Oriented solution or best practice.
It is nice, AOP. It has downsides though; as with the debug_backtrace approach, there's a heavy performance hit. That, plus the code becomes increasingly more "magical" in that it does things that aren't clear when you're looking at the code itself. That increases the time you're debugging your application.
My $0.02? First of all, don't repeat yourself: one log entry per action should be enough. Use flexible loggers that can be attached to certain classes at runtime. Decide whether or not to actually log the message in the logger, based on "severity" or "type". All in all, just implement the Observer pattern:
<?php
namespace Foo;
class MailService {
public function attach( Observes $observer ) {
$this->observers[] = $observer;
}
public function notify( $message, $type = 'notice' ) {
foreach( $this->observers as $observer ) {
$observer->notify( $message, $type );
}
}
public function sendMail( ) {
$this->notify( 'Started sending mails', 'debug' );
$mails = array( );
foreach( $mails as $mail ) {
try {
$this->notify( 'Trying to send', 'debug' );
$mail->send( );
$this->notify( 'Mail sent succesfully', 'debug' );
}
catch( Exception $e ) {
$this->notify( 'Failed to send mail', 'notice' );
}
}
$this->notify( 'Finished sending mail', 'debug' );
}
}
interface Observes {
public function notify( $message, $type = 'notice' );
}
abstract class Logger implements Observes {
protected $types = array(
'debug' => 0,
'notice' => 1,
'warning' => 2,
'error' => 3
);
protected function code( $type ) {
return isset( $this->types[$type] ) ? $this->types[$type] : 0;
}
}
class FileLogger extends Logger implements Observes {
public function __construct( $filename ) {
$this->filename = $filename;
}
/**
* #todo replace the method body with a call to, say, file_put_contents.
*/
public function notify( $message, $type = 'notice' ) {
if( $this->code( $type ) > $this->code( 'notice' ) ) { // only for warning and error.
echo $message . "\n";
}
}
}
class DebugLogger extends Logger implements Observes {
public function notify( $message, $type = 'notice' ) {
if( $this->code( $type ) === $this->code( 'debug' ) ) { // only show "debug" notices.
echo $message . "\n";
}
}
}
$service = new MailService( );
$service->attach( new FileLogger( 'yourlog.txt' ) );
$service->attach( new DebugLogger( ) );
$service->sendMail( );
If you don't want to use any external tools you can write some sort of observer wrapper around debug_backtrace that loops through the backtrace and compares all function calls to an array map provided by the wrapper and if it's a hit, writes the respective log message with your custom message text. This would be a complete separation of code where you would just need to run this observer class at the end of every script.
As for an example I think all you need is in the examples of the PHP manual. Still, here is some pseudo-code to illuminate what I mean:
//use register_shutdown_function to register your logger function
function scanTrace(Zend_Log $logger, array $eventMap)
{
$trace = array_reverse(debug_backtrace());
foreach ($trace as $step)
{
//1. extract the needed info
//2. check if the event is in your eventMap
//3. if yes, log it
}
}
The eventMap should already contain the message you want to log for every event.
If you don't mind using an external tool (which I think is the better option) then you could work with xdebug and WebGrind or similar.
Btw: You may be interested in monitorix, which is an extension of Zend_Log with a lot of nice automated logging to a db table (like slow query logging, php error and exception logging, javascript error logging).
I have logging in my Zend2 service with the help of my Go! AOP PHP library. It fast enough and allows me to debug original source code with XDebug in development mode. However, it's only beta, be aware!
use Go\Aop\Aspect;
use Go\Aop\Intercept\MethodInvocation;
use Go\Lang\Annotation\After;
use Go\Lang\Annotation\AfterThrowing;
use Go\Lang\Annotation\Before;
use Go\Lang\Annotation\Around;
/**
* Logging aspect
*/
class LoggingAspect implements Aspect
{
/**
* #var Zend\Log\Logger
*/
protected $logger = null;
/**
* Constructs a logging aspect
*/
public function __construct()
{
$logger = new Zend\Log\Logger;
$writer = new Zend\Log\Writer\Stream('php://output');
$logger->addWriter($writer);
$this->logger = $logger;
}
/**
* Method that will be called before real method
*
* #param MethodInvocation $invocation Invocation
* #Before("execution(public ClassName->*(*))")
*/
public function beforeMethodExecution(MethodInvocation $invocation)
{
$msg = 'Before: '. $this->formatMessage($invocation);
$this->logger->log(Zend\Log\Logger::INFO, $msg);
}
/**
* Method that will be called after throwing an exception in the real method
*
* #param MethodInvocation $invocation Invocation
* #AfterThrowing("execution(public ClassName->*(*))")
*/
public function afterThrowingMethodExecution(MethodInvocation $invocation)
{
$msg = 'After throwing: '. $this->formatMessage($invocation);
$this->logger->log(Zend\Log\Logger::ERR, $msg);
}
/**
* Format a message from invocation
*
* #param MethodInvocation $invocation
* #return string
*/
protected function formatMessage(MethodInvocation $invocation)
{
$obj = $invocation->getThis();
return is_object($obj) ? get_class($obj) : $obj .
$invocation->getMethod()->isStatic() ? '::' : '->' .
$invocation->getMethod()->getName() .
'()' .
' with arguments: ' .
json_encode($invocation->getArguments()) .
PHP_EOL;
}
}
```
you know that zend log can have more than one writer http://framework.zend.com/manual/en/zend.log.writers.html#zend.log.writers.compositing
its simple as creating a class single function
class logger {
public function log ($value , $type , $bothlogger = false){
$writer1 = new Zend_Log_Writer_Stream('/path/to/first/logfile');
$writer2 = new Zend_Log_Writer_Stream('/path/to/second/logfile');
$logger = new Zend_Log();
$logger->addWriter($writer1);
if($bothlogger === true){
$logger->addWriter($writer2);
}
// goes to both writers
$logger->info('Informational message');
return true;
}
}
of course you can modify this sample to be faster with many ways , but it should explain the idea

Categories