Userimport aus CSV-Datei (BeelabUserBundle)

geposted am 16.01.2018, in Symfony

UserManager verwenden

zum Bulk-Import von Usern als shell-Kommando muß man den Beelab-Usermanager verwenden.

use Beelab\UserBundle\Manager\UserManager;

Auslesen von CSV-Dateien

geht mit Symfony-Bordmitteln und dann 2 Zeilen Code, man muß nur daran denken, dem CsvEncoder das Trennzeichen mitzugeben:

 // csv import mit symfony 3.2 bordmitteln:
        $serializer = new Serializer([new ObjectNormalizer()], [new CsvEncoder(';')]);
        $data = $serializer->decode(file_get_contents('%kernel.root_dir%/../tmp/userimport.csv'), 'csv');

Und das ist KEIN Schreibfehler, für das Auslesen ist tatsächlich der ENcoder zuständig, einen csv-Decoder gibts nicht.

Related Objects (1:n-Beziehungen)

Falls die Entity related Objects hat, wie z.B. Teamzugehörigkeit etc. die in der CSV-Datei schon referenziert sind, holt man so das doctrine object:

$team = $this->em->getRepository('AppBundle:Team')->findOneByName($teamid);

Passwort-Generierung

Das Initialpasswort wurde hier ziemlich simpel umgesetzt. (der User sollte in der Mail mit den Zugangsdaten die Aufforderung bekommen, das PW sofort zu ändern).

    private function generatePassword($length = 8)
    {
        $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_-=+;:,.?";
        $password = '';
        for ($i = 0; $i < $length; $i++) {
            $password .= $chars[mt_rand(0, strlen($chars) - 1)];
        }
        return $password;
    }

Logging

  private function log($message)
    {
        if ($message){
             error_log($message.PHP_EOL, 3, 'var/logs/user-import.log');
        }
         return true;
    }

Komplett

<?php

namespace AppBundle\Command;

use AppBundle\Entity\Team;
use AppBundle\Entity\User;
use Symfony\Component\Console\Command\Command;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Encoder\CsvEncoder;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Beelab\UserBundle\Manager\UserManager;

/**
 * Class CsvImportCommand
 * @package AppBundle\ConsoleCommand
 */
class UserImportCommand extends Command
{

    /**
     * @var EntityManagerInterface
     */
    private $em;
    private $um;

    /**
     * CsvImportCommand constructor.
     *
     * @param EntityManagerInterface $em
     *
     * @throws \Symfony\Component\Console\Exception\LogicException
     */
    public function __construct(EntityManagerInterface $em, UserManager $um)
    {
        $this->em = $em;
        $this->um = $um;
        parent::__construct();
    }

    /**
     * Configure
     * @throws \Symfony\Component\Console\Exception\InvalidArgumentException
     */
    protected function configure()
    {
        // Aufruf in der Docroot mittels: php bin/console csv:userimport --env=dev --nodebug
        $this->setName('csv:userimport')
                ->setDescription('Imports User from CSV data file')
        ;
    }

    /**
     * @param InputInterface  $input
     * @param OutputInterface $output
     *
     * @return void
     * @throws \Doctrine\ORM\NonUniqueResultException
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $io = new SymfonyStyle($input, $output);
        $message = date(DATE_RFC822) . ' ' .$this->getName(). ' gestartet...';
        $line = str_pad('', strlen($message), '-');
        // eigener logger siehe unten
        $this->log($message.PHP_EOL.$line);

        // csv import mit symfony 3.2 bordmitteln:
        $serializer = new Serializer([new ObjectNormalizer()], [new CsvEncoder(';')]);
        $data = $serializer->decode(file_get_contents('%kernel.root_dir%/../tmp/userimport.csv'), 'csv');
        if (!$data) {
            $io->error('no data.');
            exit(0);
        }
        $io->progressStart(count($data));

        $cnt = 0;
        foreach ($data as $row) {
            $cnt++;
            $teamid = trim($row['team']);
            $rollenid = trim($row['rollenid']);
            $team = $this->em->getRepository('AppBundle:Team')->findOneByName($teamid);
            $rolle = $this->em->getRepository('AppBundle:Rollen')->find($rollenid);
            if (!$team) {
                $message = sprintf('Zeile %d :: Team %d konnte nicht zugeordnet werden!', $cnt, $teamid);
                $io->error($message);
            }
            if (!$rolle) {
                $message = sprintf('Zeile %d :: Rolle %d konnte nicht zugeordnet werden!', $cnt, $rollenid);
                $io->error($message);
            }

            $user = new User();
            $user->setVorname(trim($row['vorname']));
            $user->setNachname(trim($row['nachname']));
            $user->setEmail(trim($row['email']));
            $user->setActive(1);
            $user->setStatus(1);
            $user->setRollen($rolle);
            $user->setTeam($team);
            $role = 'ROLE_' . strtoupper($rolle->getKurz());
            $user->addRole($role);
            $pw = $this->generatePassword();
            $user->setPlainPassword($pw);

            $this->um->create($user, false);
            // for DEV Environment / Debugging only !!! 
            $message = $user->getEmail() . ';' . $pw;
            $this->log($message);
//            $this->em->persist($user);
            $io->progressAdvance();
        }
        $this->em->flush();
        $this->em->clear();
        $io->progressFinish();

        $io->success($cnt . ' User importiert!');
        $message = date(DATE_RFC822) . ' ' . $this->getName(). ' beendet.';
        $line = str_pad('', strlen($message), '-');
        $this->log($line.PHP_EOL.$message);

    }

    /**
     *
     * @param type $length
     * @return string
     */
    private function generatePassword($length = 8)
    {
        $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_-=+;:,.?";
        $password = '';
        for ($i = 0; $i < $length; $i++) {
            $password .= $chars[mt_rand(0, strlen($chars) - 1)];
        }
        return $password;
    }

    // da der monolog nicht funktioniert, bauen wir uns einen eigenen logger
    private function log($message)
    {
        if ($message){
             error_log($message.PHP_EOL, 3, 'var/logs/user-import.log');
        }
         return true;
    }

}

Todo

Das Passwort soll dem User natürlich per Mail zugeschickt werden, und nicht wie hier in einer eigenen Datei im Klartext gespeichert werden. Es ging hier aber um Testuser mit Dummy-Email-Adressen.

Link

Hilfreich wie immer die Symfony-Documentation:

https://symfony.com/doc/current/console.html