phpcsvsymfonytruncate

How to handle a bad CSV file import in Symfony/PHP


I am uploading a lot of data from a csv without problem but I would like to protect a possible user error, in the sense that it is possible that they put a bad csv in the form ...

The problem is that I truncate my table every time the form is launched ...


ImportController.php

       $form = $this->createFormBuilder()
       ->add('form', FileType::class, [
           'attr' => ['accept' => '.csv',
           'class' => 'custom-file-input'],
           'label' => 'Import'
       ])
       ->getForm();
       
       $form->handleRequest($request);

           if ($form->isSubmitted() && $form->isValid()) 
           {
               /** 
                * @var UploadedFile
                */
               $file = $form->get('form')->getData();  
               $connection = $em->getConnection();
               $platform = $connection->getDatabasePlatform();
               $connection->beginTransaction();
               $connection->executeQuery($platform->getTruncateTableSQL('MyTable', true));
               $this->getDoctrine()->getManager()->getRepository(MyTable::class)->importMyData($file);           
       
               $this->addFlash('success',"The csv has been successfully imported");
               return $this->redirectToRoute('import');
           } 


MyTableRepository.php

public function importMyData($file)
    {
        $em = $this->entityManager;

        if (($handle = fopen($file->getPathname(), "r")) !== false) 
        {
            $count = 0;
            $batchSize = 1000;
            $data = fgetcsv($handle, 0, ","); 

            while (($data = fgetcsv($handle, 0, ",")) !== false) 
            {
                $count++;
                $entity = new MyTable();

                // 40 entity fields...
                $entity->setFieldOne($data[0]);                
                $entity->setFieldTwo($data[1]); 
                //....
                $entity->setFieldForty($data[39]); 

                $em->persist($entity);

                if (($count % $batchSize) === 0 )
                {
                    $em->flush();
                    $em->clear();
                }
            }
            fclose($handle);
            $em->flush();
            $em->clear();
        }
    }

I just want that when a bad CSV file is launched that the table is not Truncate


Solution

  • You can sanitize and validate data before insert into the database. You can attempt this many ways. First I would use Symfony file validator to make sure to check valid file has been uploaded.

    Symfony File Validator

    To validate each row you can use custom callback validator or raw values of the array Validate Raw Data

    //call the validator in the repository
    $validator = Validation::createValidator();
    
    // sample input
    $input = [
    'field_1' => 'hello',
    'field_2' => 'test@email.tld',
    'field_40' => 3
    ];
    
    //define the constraints for each row
    $constraint = new Assert\Collection([
    // the keys correspond to the keys in the input array
    'field_1' => new Assert\Collection([
        'first_name' => new Assert\Length(['min' => 101]),
        'last_name' => new Assert\Length(['min' => 1]),
    ]),
    'field_2' => new Assert\Email(),
    'field_40' => new Assert\Length(['min' => 102])
    ]);
    
    while (($data = fgetcsv($handle, 0, ",")) !== false) {
      $violations = $validator->validate($data, $constraint);
      
      // you can skip the row or log the error
      if ($violations->count() > 0) {
        continue;
      }
    }