deferre_mario_po
deferre_mario_po

Reputation: 45

Symfony - ArrayCollection - Update or Create

I have the Entity company :

<?php

// src/Entity/Company.php
class Company
{

    /**
    * @ORM\OneToMany(targetEntity="App\Entity\Employee", mappedBy="company")
    */
    private $employees;

    public function __construct()
    {
        $this->employees = new ArrayCollection();
    }

    public function getEmployees()
    {
        return $this->employees;
    }

and the Employee entity :

<?php

// src/Entity/Employee.php
class Employee
{

    /**
    * @ORM\ManyToOne(targetEntity="App\Entity\Company", inversedBy="employees")
    */
    private $company;

    public function getCompany()
    {
        return $this->company;
    }

    public function setCompany(?Company $company)
    {
        $this->company = $company;
        return $this;
    }

Below is my loop for each line of the CSV being imported :

<?php

    // $csv = array made from the CSV file
    // e.g. $csv[0]['employee_name'] = "John Doe"
    // e.g. $csv[0]['employee_mail'] = "[email protected]"
    // e.g. $csv[0]['employee_company_name'] = "BMW"
    // e.g. $csv[0]['employee_company_id'] = 77

    foreach($csv as $key => $value)
    {
        if($company = $this->em->getRepository(Company::class)->find($value['employee_company_id']))
        {
            // if the employee doest not exist, create it
            // IN MY TESTS, HERE IS MY PROBLEM
            // DON'T KNOW HOW TO LOOP INSIDE THE EMPLOYEES LIST USING the MAIL
            if ($company->getEmployees()->contains($value['employee_mail']))
            {
                // This employee for this company exists, let's update it
            }
            else
            {
                // This employee for this company does not exist, let's create it
            }
        }
        else
        {
            // Create the company
        }

I don't know how to loop inside the company employees list, in order to decide if I have to edit (employee already exists) or create a new employee. Maybe I should not use the ArrayCollection::contains method ?

Upvotes: 1

Views: 2482

Answers (3)

Rami Dridi
Rami Dridi

Reputation: 351

You should store first your list of employees :

$employees = $company->getEmployees() 

then you loop inside $employees :

foreach($employees as $employee ) //supposedly you have 'email' property
{
   if ($employee->getEmail() == $value['employee_mail'])
      {
         //your code here
      }
}

don't forget to add this :

*  @ORM\OneToMany(targetEntity="App\Entity\Employee", mappedBy="company", fetch="EAGER")

Update :

$qb = $repository->createQueryBuilder('a')
    ->where(':employee_mail OF a.emplyee_email')
    ->setParameter('employee_mail ', $value['employee_mail']);
    ->andWhere(':comapanyId MEMBER OF a.company_id');
    ->setParameter('comapanyId ', $value['employee_company_id']);
    ->getQuery()
    ->execute()

this will return null if the employee does not exist in the company, you should implement this as you need in your code.

Upvotes: 0

Leander
Leander

Reputation: 148

As Employees is an Doctrine ArrayCollection, you can use the exists method on it. This method accepts a closure as argument which loops over all elements in the collection and returns true when the condition matches.

if ($company->getEmployees()->exists(function ($key, Employee $employee) use ($value) {
    return $employee->getEmail() === $value['employee_mail'];
})) {
    // Employee exists, update
} else {
    // Employee does not exist
}

Alternatively, if you want to create/update the record right away, you can do the following. This returns the Employee if it exists, or create a new Employee object if it doesn't

$employee = $company
    ->getEmployees()
    ->filter(function (Employee $employee) use ($value) {
        return $employee->getEmail() === $value['employee_mail'];
    })
    ->first() ?? new Employee();

Upvotes: 1

Jakumi
Jakumi

Reputation: 8374

the naive approach would be to just look in the employee repository.

$employee = $em->getRepository(Employee::class)->findOneBy([
    'company' => $company, 
    'email' => $value['employee_mail'],
]);
if($employee) {
    // exists -> update
} else {
    // create
}

Depending on the company-employee-ratio, it might be better to cycle through the company's employees instead:

$employee = null;
foreach($company->getEmployees() as $_employee) {
    if($_employee->getEmail() == $value['employee_mail']) {
         $employee = $_employee;
         break; // short circuit the loop
    }
}
// rest is the same

If there are is very large number of employees in your csv and/or the database, it might be even better to skip the ORM alltogether and go straight to the database instead. Otherwise, you might want to clear the entity manager once in a while, if you have a huge database / csv.

Upvotes: 0

Related Questions