Skip to content

Instantly share code, notes, and snippets.

@tanasecosminromeo
Created May 5, 2020 21:06
Show Gist options
  • Save tanasecosminromeo/6ae314fced40a4e682813829520c7075 to your computer and use it in GitHub Desktop.
Save tanasecosminromeo/6ae314fced40a4e682813829520c7075 to your computer and use it in GitHub Desktop.
This is a Symfony service that allows importing a google sheet into an entity.
<?php
namespace App\Lib;
use Doctrine\ORM\EntityManagerInterface;
use Exception;
use Google_Client;
use Google_Exception;
use Google_Service_Sheets;
class GoogleSheetsImport {
/**
* @var Google_Service_Sheets
*/
private Google_Service_Sheets $sheets;
/**
* @var null|EntityManagerInterface
*/
private ?EntityManagerInterface $em;
/** @var array */
private array $data;
/** @var array */
private array $lastWarnings = [];
/** @var int */
private int $updates = 0;
/**
* GoogleSheetsToArray constructor.
* @param string $applicationName
* @param string $authConfig
* @param EntityManagerInterface $entityManager
* @throws Google_Exception
*/
public function __construct(string $applicationName, string $authConfig, ?EntityManagerInterface $entityManager = null)
{
$client = new Google_Client();
$client->setAccessType('offline');
$client->setApplicationName($applicationName);
$client->setScopes([Google_Service_Sheets::SPREADSHEETS]);
$client->setAuthConfig($authConfig);
$this->sheets = new Google_Service_Sheets($client);
$this->em = $entityManager;
}
public function setEntityManager(EntityManagerInterface $entityManager){
$this->em = $entityManager;
}
/**
* @param string $entityType
* @param string $entityIdHeader
* @param array $fieldMatches
* @return int
* @throws Exception
*/
public function importEntity(string $entityType, string $entityIdHeader, array $fieldMatches) : int
{
if (!$this->em instanceof EntityManagerInterface){
throw new Exception('To import a google sheet to an entity you need to set the entity manger');
}
$this->lastWarnings = [];
$propertyRepository = $this->em->getRepository($entityType);
$updates = 0;
foreach ($this->data as $rowId => $row){
$entity = $propertyRepository->find($row[$entityIdHeader]);
if (get_class($entity) !== $entityType){
$this->lastWarnings[] = sprintf('%s not found for row #%d - id #$d', $entityType, ($rowId+1), (int) $row[$entityIdHeader]);
} else {
$shouldPersist = false;
foreach ($fieldMatches as $sheetHeader => $setter){
$handler = fn($value) => $value; //By default we do not format the value
if (gettype($setter) === 'array'){
list($setter, $handler) = $setter;
}
if (!empty($row[$sheetHeader])){
$updates++;
$shouldPersist = true;
$entity->$setter($handler($row[$sheetHeader]));
} else {
$this->lastWarnings[] = sprintf('Can\'t find value for %s for %s id #%d', $sheetHeader, $entityType, $entity->getId());
}
}
if ($shouldPersist){
$this->em->persist($entity);
}
}
}
$this->updates += $updates;
return $updates;
}
public function flushIfNeeded() : int
{
$updates = $this->updates;
if ($updates > 0){
$this->em->flush();
$this->updates = 0;
}
return $updates;
}
/**
* @param string $sheetId
* @param string $range
* @return GoogleSheetsImport
* @throws Exception
*/
public function get(string $sheetId, string $range) : GoogleSheetsImport
{
$response = $this->sheets->spreadsheets_values->get($sheetId, $range);
$values = $response->getValues();
if (empty($values)) {
throw new Exception('You do not have access to this spreadsheet');
}
$header = [];
$this->data = [];
foreach ($values as $rowId => $row) {
if ($rowId === 0){
$header = $row;
} else {
$rowData = [];
foreach ($row as $columnId => $value){
$rowData[$header[$columnId]] = $value;
}
$this->data[] = $rowData;
}
}
unset($header);
return $this;
}
public function getData(){
return $this->data;
}
/**
* @return array
*/
public function getLastWarnings(): array
{
return $this->lastWarnings;
}
}
@tanasecosminromeo
Copy link
Author

Eg of how to use:

<?php

namespace App\Command;

use App\Entity\Property;
use App\Lib\GoogleSheetsImport;
use Doctrine\ORM\EntityManagerInterface;
use Exception;
use Google_Exception;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

class importManagementPropertyInfoCommand extends Command
{
    protected static string $defaultName = 'app:importManagementPropertyInfo';
    /**
     * @var EntityManagerInterface
     */
    private EntityManagerInterface $em;
    /**
     * @var SymfonyStyle
     */
    private SymfonyStyle $io;

    /** @var GoogleSheetsImport */
    private GoogleSheetsImport $sheets;

    /**
     * importManagementPropertyInfoCommand constructor.
     * @param EntityManagerInterface $entityManager
     * @throws Google_Exception
     */
    public function __construct(EntityManagerInterface $entityManager)
    {
        $this->em = $entityManager;
        $this->sheets = new GoogleSheetsImport('YourApp', '/config/google-api.json');
        $this->sheets->setEntityManager($entityManager);
        
        parent::__construct();
    }

    /**
     * @param InputInterface $input
     * @param OutputInterface $output
     * @return int
     * @throws Exception
     */
    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $this->io = new SymfonyStyle($input, $output);

        $this->sheets->get('SHEETID', 'SheetName!A1:N')->importEntity(Property::class, 'Property ID', [
            'Mgmt fee per room inc VAT' => ['setManagementFee', 'floatval'],
            'British gas costs' => ['setBritishGasCost', 'floatval'],
            'Bills cost per tenant' => ['setBillsCost', 'floatval']
        ]);

        foreach ($this->sheets->getLastWarnings() as $warning){
            $this->io->warning($warning);
        }

        $changes = $this->sheets->flushIfNeeded();

        if ($changes){
            $this->io->note(sprintf('Flushed %d changes', $changes));
        } else {
            $this->io->note('No changes');
        }

        return 0;
    }
}

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