ArrayCollection ( Doctrine ) contains function ret

2019-08-04 07:14发布

I've got this piece of code

if(!$from->getFiles()->contains($proxy))
 {
    $return = "";

    foreach($from->getFiles() as $file)
    {
        $return .= $file->getFilename() . " --- ";
    }

    return array('type' => 'error', 'message' => 'Folder 
'.$from->getName().' does not contain '.$proxy->getFilename(). ' 
All files from this folder '. $return);
 }

I'm sorry it's badly formatted, but it's just a debug message.

$from is a model of a folder.

the relation to getFiles() ( the variable it returns ) is this

/**
 * @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;

getFiles() is a function, that returns $files, that are contained in that folder, and it returns and ArrayCollection.

/**
 * Get files
 *
 * @return \Doctrine\Common\Collections\Collection 
 */
public function getFiles()
{        
    return $this->files;
}

$proxy is an object of type FileProxy and it models a file.

The problem is, this if statement runs even if it contains the object that I'm checking, and the foreach is in this if to prove that, the array that I'm returning contains this message = " Folder F does not contain X All files from this folder X Y Z " which just shows, that the contains function returns an incorrect value, because X and X are the same.

I hope I explained this in enough detail.

Does anyone know what might be the cause of this ?

Thanks.

1条回答
看我几分像从前
2楼-- · 2019-08-04 08:09

As everything with Doctrine, because their developers are awfully awful at being developer friendly, this answer is just a theory I have, maybe someone could correct, prove, or disprove what I am about to write.

I should have probably mentioned it, but did not think it was actually important, because those operations happen after this check, but this code is a part of "Move" function, it takes two Folders and a File, and remove the File from one Folder and add to the other Folder.


Explanation:

What I think is the cause of this, is very bizarre behavior of Doctrine combined with PHP optimzer, if such a thing exists.

  1. Doctrine opens a transaction whenever a database operation starts, and rolls it back when something bad happens, but flushes it to the database when you run flush(). That sounds very reasonable, but that does not seem to work for deletes, or rather for functions that work with ArrayCollection, since ArrayCollections are a direct mapping to a table, when you run a removeElement function on it, it IMMEDIATELY, without flushing, removes it from the database, no questions asked, which in a "Move" function like mine, means that if I flush after performing all of the operations, it will first delete all of the files, and then add them once the flush() function is called, but if an error occures, ( I don't really remember ) I think all of the files will remain deleted ...

  2. I don't know if that is true, but I am pretty sure PHP has some kind of an optimizer, it probably moves code around and runs it in a more optimized way, then the programmer wrote it in.


Theory:

Because Doctrine deletes files immediately, without the flush operation, and if PHP moves code around, it could happen, that my code, that checks if the File is in that Folder is run AFTER the File has already been deleted from the database, but the Folder object STILL CONTAINS the Files!


Solution:

You could either transform the ADD/REMOVE combo, to a merge operation, so that the object (File) is just modified, or You could move the "contains" function outside of the moving function, so that it is not affected by the supposed Code Order Manipulation ( which actually works for me ).

Hope this helps someone.

查看更多
登录 后发表回答