Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Duplicated row in database #682

Closed
robertkabat opened this issue Mar 7, 2017 · 13 comments
Closed

Duplicated row in database #682

robertkabat opened this issue Mar 7, 2017 · 13 comments

Comments

@robertkabat
Copy link

robertkabat commented Mar 7, 2017

Hi,

I think I looked everywhere but still don't see any solution.

I am using Symfony 3 with Alice 2.x.

Let's say I have two bundles. Both have one php loader file and one fixtures.yml file. When I'm trying to access object from fixture A in fixture B (e.g user: "@user_<current()>") while loading dummy data into database I get duplicated row error.

It looks like when I call for user object Alice is trying to save it again into database. This doesn't happen when I load only one fixture file with both object descriptions in one place.

Maybe I missed something in the docs. But according to what I've read it should work.

To be clear on what I would like to achieve:

I would like to have separate fixtures files with separate loader in different bundles able to create relations between them.

Am I missing something here or it's a real issue?

@theofidry
Copy link
Member

Hi @robertkabat. No you read the doc fine, that may be a bug. Are you using HautelookAliceBundle 1.x or 2.x? And NelmioAlice 2.x or 3.x?

Potential duplicate of https://github.com/hautelook/AliceBundle/issues/314

@robertkabat
Copy link
Author

@theofidry I'm using this one:

"nelmio/alice": "^2.2"

And I'm not using HautelookAliceBundle.

I have my own two bundles where I simply keep loader + fixtures in DataFixtures folder. This is how the e.g loader file looks like:

namespace CodeCraft\UserBundle\DataFixtures\ORM;

use Doctrine\Common\DataFixtures\FixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;
use Nelmio\Alice\Fixtures;

class LoadUserFixtures implements FixtureInterface
{
    public function load(ObjectManager $manager)
    {
        $objects = Fixtures::load(__DIR__ . '/fixtures.yml', $manager);
    }
}

And example fixtures:

CodeCraft\UserBundle\Entity\User:
  user_{1..10}:
    email (unique): '<firstName()>+<randomNumber()>@gmail.com'
    plainPassword: 'secret'
    status: '<randomElement([0, 1])>'
    activation_code: '$2a$04$yXqGMzH87U5tK23Ezl.fneBzJuGQNOBWATe4WuN/e/aOtLg/TZNVa'
    created_at: '<datetime()>'
    updated_at: '<datetime()>'

@theofidry
Copy link
Member

Hm, not sure then without having a bit more code to bite to. That said you might want to take a look at https://github.com/hautelook/AliceBundle/blob/1.x/src/Alice/DataFixtures/Loader.php, that's what it is really.

@robertkabat
Copy link
Author

robertkabat commented Mar 9, 2017

Actually that's all the code for this case. It's very simple. Creating user and post attached to that user. So there is nothing to bite :) I will go through Loader class and let you know if I ill find anything.

@theofidry
Copy link
Member

There is two things bugging me in your code:

  • What about the order in which the files are loaded?
  • I can't remember how exactly the loader keep references of the objects between each loading. I think the loader above can help you with that as well.

From what I see, that's the two leads I can give you :)

@robertkabat
Copy link
Author

Thanks for the leads :)

I was also thinking that files are loaded in wrong order but then I wouldn't get error message for duplicated row but for missing reference I think. Also I saw that order in which files are loaded is correct(1 - users, 2 - stories). Just to be sure I've implemented getOrder functions. so it looks like this right now:

dev@awesome:/var/www/story/vendor$ story doctrine:fixtures:load
Careful, database will be purged. Do you want to continue y/N ?y
  > purging database
  > loading [1] CodeCraft\UserBundle\DataFixtures\ORM\LoadUserFixtures
  > loading [2] CodeCraft\StoryBundle\DataFixtures\ORM\LoadStoryFixtures

However I've noticed that even if i keep data description in different yml files but I load both of them in one loader file - then it's ok. Example:

class LoadUserFixtures extends AbstractFixture implements OrderedFixtureInterface
{
    public function load(ObjectManager $manager)
    {
        $objects = Fixtures::load([
            'path_to_first_yml',
            'path_to_second_yml',
        ], $manager);
    }

    /**
     * Get the order of this fixture
     *
     * @return integer
     */
    public function getOrder()
    {
        return 1;
    }

If I try to load these two files with separate loaders like this:

class LoadUserFixtures extends AbstractFixture implements OrderedFixtureInterface
{
    public function load(ObjectManager $manager)
    {
        $objects = Fixtures::load(__DIR__ . '/fixtures.yml', $manager);
    }

    /**
     * Get the order of this fixture
     *
     * @return integer
     */
    public function getOrder()
    {
        return 1;
    }
}

I get duplicated rows.

It looks like it has something to do with the second lead that you provided. I will definitely check that out!

@theofidry
Copy link
Member

Hm, might also have to do with Doctrine then: maybe an entity is detached at some point and introduce as a new one... I can't say much more at that point :)

@robertkabat
Copy link
Author

I just found out that relations between data objects are correct. Every new story gets unique user relation. I will investigate this further, I think I'm getting to the bottom of this. As soon as I find out what's wrong I will give you a shout.

Thank you for your time and help ;)

@theofidry theofidry added this to the 2.x milestone Mar 14, 2017
@theofidry
Copy link
Member

@robertkabat any news here?

@robertkabat
Copy link
Author

robertkabat commented Oct 20, 2017

Hello,

Regarding bug itself - no. I have spent some time on it but it was not enough. I needed fast solution so I came up with workaround. If you are interested you can find it here:

stack overflow workaround

This is how I structured my files:

file structure

Then in tests I use

$this->databaseMigration();

This is my function which loads data to database.

You can find working example of complete solution in this repository.

I may take another look at this soon. Any progress on your side?

Regards,
Rob

@vgeyer
Copy link

vgeyer commented Mar 6, 2018

I ran into the same problem having the following associations in my entities:
Customer

    /**
     * @ORM\OneToOne(targetEntity="Customer", inversedBy="customerAddress", cascade={"persist"})
     * @ORM\JoinColumn(name="customer_id", referencedColumnName="id")
     *
     * @var Customer|null
     */
    private $customer;

CustomerAddress

    /**
     * @ORM\OneToOne(targetEntity="CustomerAddress", mappedBy="customer", cascade={"persist"})
     * @var CustomerAddress|null
     */
    private $customerAddress;

fixtures.yaml

App\Entity\Customer\Customer:
    customer{1..2}:
        mail (unique): "customer<current()>\\@user.com"

App\Entity\Customer\CustomerAddress:
    customerAddress2:
        customer (unique): "@customer2"
        app (unique): "1"

The bug itself does not in doctrine, but in the loader logic, converting the yaml content into objects. Putting a dump($objects); into \Fidry\AliceDataFixtures\Loader\PersisterLoader::load after $objects = $this->loader->load(....); is showing the truth about what is happening:

array:3 [
  "customer1" => App\Entity\Customer\Customer {#872
    #id: null
    -customerAddress: null
    #mail: "customer1@user.com"
  }
  "customer2" => App\Entity\Customer\Customer {#862
    #id: null
    -customerAddress: null
    #mail: "customer2@user.com"
  }
  "customerAddress2" => App\Entity\Customer\CustomerAddress {#870
    -id: null
    -app: "1"
    -customer: App\Entity\Customer\Customer {#1194
      #id: null
      -customerAddress: null
      #mail: "customer2@user.com"
    }
  }
]

Take a closer look at object ids prefixed #. The customerAddress2 CustomerAddress instance does have a customer with the mail customer2@user.com. The customer2 instance, also having the mail address - as you would expect - customer2@user.com but having a different object id! Doctrine is not dettatching anything, it is handling instances.
Doing this $objects['customer2'] = $objects['customerAddress2']->getCustomer(); after $objects = $this->loader->load(....); in ``\Fidry\AliceDataFixtures\Loader\PersisterLoader::load``` is correcting it:

  "customer1" => App\Entity\Customer\Customer {#872
    #id: null
    -customerAddress: null
    #mail: "customer1@user.com"
  }
  "customer2" => App\Entity\Customer\Customer {#1194
    #id: null
    -customerAddress: null
    #mail: "customer2@user.com"
  }
  "customerAddress2" => App\Entity\Customer\CustomerAddress {#870
    -id: null
    -app: "1"
    -customer: App\Entity\Customer\Customer {#1194}
  }
]

Now the object ids are correct, and now there are no more duplicated rows or duplicate value errors. So the error has to be in $objects = $this->loader->load(....); or its implementations creating the objects from the yamls. Tomorrow i will have a closer look, let me know if you got a quick idea @theofidry!

@theofidry
Copy link
Member

@vgeyer I think this is a different bug (it's in 2.x not 3.x). I think your issue was related to #883

@theofidry
Copy link
Member

Unless there is new input, in which case feel free to open a new issue with a reproducer, I'll be closing this one

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants