Jak zaimportować dwie bazy danych w Symfony 4
Przyjmijmy, że mamy dwie strony internetowe — jedna zrobiona w WordPressie, a druga w PrestaShop — naszym zadaniem jest zrobienie backendu, który pobierze dane z obu stron i coś z nimi zrobi.
No to zaczynamy!
Na początek utwórzmy nowy projekt i zainstalujmy jednego bundla.
composer create-project symfony/website-skeleton my_project
composer req mikoweb/db-first-helper-bundle
Po utworzeniu projektu przystępujemy do konfiguracji połączeń z bazami danych. Połączenie domyślnie (default)
zostawiamy do "wewnętrznej" logiki aplikacji i dodajemy dwa nowe, które nazwę
db1
(WordpPess) i db2
(PrestaShop)
— a zatem łącznie będziemy mieli trzy połączenia z bazą danych.
Tak oto wygląda kompletny plik config/packages/doctrine.yaml
:
parameters:
env(DATABASE_URL): ''
doctrine:
dbal:
default_connection: default
connections:
default:
driver: 'pdo_mysql'
server_version: '5.7'
charset: utf8mb4
default_table_options:
charset: utf8mb4
collate: utf8mb4_unicode_ci
url: '%env(resolve:DATABASE_URL)%'
db1:
driver: 'pdo_mysql'
server_version: '5.7'
charset: utf8mb4
default_table_options:
charset: utf8mb4
collate: utf8mb4_unicode_ci
url: mysql://rafal:rafal@127.0.0.1:3306/dev_wp
db2:
driver: 'pdo_mysql'
server_version: '5.7'
charset: utf8mb4
default_table_options:
charset: utf8mb4
collate: utf8mb4_unicode_ci
url: mysql://rafal:rafal@127.0.0.1:3306/dev_prestashop
mapping_types:
enum: string
schema_filter: '/^(((?!accessory|cart_product|customer_message_sync_imap|linksmenutop_lang|order_detail_tax|order_invoice_tax).))*$/'
orm:
default_entity_manager: default
auto_generate_proxy_classes: '%kernel.debug%'
entity_managers:
default:
connection: default
naming_strategy: doctrine.orm.naming_strategy.underscore
auto_mapping: true
mappings:
Default:
is_bundle: false
type: annotation
dir: '%kernel.project_dir%/src/Entity/Default'
prefix: 'App\Entity\Default'
alias: Default
db1:
connection: db1
naming_strategy: doctrine.orm.naming_strategy.underscore
mappings:
Db1:
is_bundle: false
type: annotation
dir: '%kernel.project_dir%/src/Entity/Db1'
prefix: 'App\Entity\Db1'
alias: Db1
db2:
connection: db2
naming_strategy: doctrine.orm.naming_strategy.underscore
mappings:
Db2:
is_bundle: false
type: annotation
dir: '%kernel.project_dir%/src/Entity/Db2'
prefix: 'App\Entity\Db2'
alias: Db2
Jeśli ktoś jest ciekaw, co dokładnie robi ta magia, to odsyłam do
dokumentacji.
Ze swojej strony mogę dodać, że przy imporcie tabel z bazy PrestaShop pojawiły się drobne problemy.
Pierwszy to enum
w definicji którejś z kolumn (Doctrine nie rozumie tego typu),
stad w połączeniu db2
wzięła się opcja mapping_types
, która naprawia ten błąd.
Drugi problem to popsute tabele, które nie posiadają klucza głównego.
Nie udało mi się znaleźć dobrego rozwiązania, więc postanowiłem zignorować niektóre tabele,
za co odpowiada opcja schema_filter
.
Teraz można łączyć się z różnymi bazami.
Komendy
Utwórzmy dwie komendy, każda z nich będzie odpowiedzialna za import innej bazy danych. Do napisania komend posłużymy się klasą abstrakcyjną zaciągniętą z zewnętrznego bundla.
Komenda 1php bin/console make:command app:db1:import
src/Command/Db1ImportCommand.php
<?php
namespace App\Command;
use Mikoweb\Bundle\DbFirstHelperBundle\Command\ImportDatabaseCommandAbstract;
class Db1ImportCommand extends ImportDatabaseCommandAbstract
{
protected static $defaultName = 'app:db1:import';
}
Dla tej komendy trzeba zmienić dwie opcje w pliku konfiguracyjnym
config/packages/mikoweb_db_first_helper.yml
:
mikoweb_db_first_helper:
entity_folder: Entity/Db1 # Katalog gdzie będą zapisywane klasy Entity
connection: db1 # Nazwa połączenia bazy danych
Komenda 2
php bin/console make:command app:db2:import
src/Command/Db2ImportCommand.php
<?php
namespace App\Command;
use Mikoweb\Bundle\DbFirstHelperBundle\Command\ImportDatabaseCommandAbstract;
class Db2ImportCommand extends ImportDatabaseCommandAbstract
{
protected static $defaultName = 'app:db2:import';
protected function getConnection(): string
{
return 'db2';
}
protected function getEntityFolder(): string
{
return 'Entity/Db2';
}
}
W tym przypadku, zamiast modyfikować config, nadpisujemy dwie metody w klasie.
Uruchamiamy to!
php bin/console app:db1:import

php bin/console app:db2:import

W tym momencie obie bazy danych zostały już zaimportowane i można zacząć operować danymi. Żeby sprawdzić, czy to rzeczywiście działa, dodamy repository do encji WpPosts.
src/Repository/Db1/PostsRepository.php
<?php
namespace App\Repository\Db1;
use App\Entity\Db1\WpPosts;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Symfony\Bridge\Doctrine\RegistryInterface;
class PostsRepository extends ServiceEntityRepository
{
public function __construct(RegistryInterface $registry)
{
parent::__construct($registry, WpPosts::class);
}
}
A w kontrolerze pobierzemy sobie wszystkie posty z WordPressa. Zaręczam, że działa :)
public function __construct(PostsRepository $repository)
{
$this->repository = $repository;
}
public function index()
{
return $this->render('default/index.html.twig', [
'posts' => $this->repository->findAll(),
]);
}
Podsumowanie
We wstępie napisałem, że naszym zadaniem jest pobranie danych z obu baz i zrobienie czegoś z nimi, lecz w tym wpisie pobrałem dane tylko z jednej bazy i nie zrobiłem z nimi nic użytecznego — myślę, że jest to temat na kolejny wpis.
Jeśli spodobał Ci się bundle użyty do celów tego wpisu, daj gwiazdkę.