Salut la voi! Vă citesc de mult timp. Nu sînt foarte activ online, dar am decis să-mi creez un cont pentru a pune o întrebare, poate poate mă descoase cineva.
Ce vreau să fac
Am creat un sistem prin care să pot încărca fișiere în aplicația mea (în mare, vorbim despre fotografii). Aplicația este scrisă folosind Symfony 5, cu PHP 8. Nu cred că va conta pentru ce urmează să descriu în continuare, dar zic să fie scris. Ideea mea era să pun la punct un sistem care să-mi permită pe mediul de lucru local, să pot să încarc fișiere local (undeva în /storage/…
), iar în mediul de producție, fișierele să fie încărcate pe Amazon S3.
Pentru a realiza asta, am folosit pachetul league/flysystem-bundle
, împreună cu adaptorul league/flysystem-aws-s3-v3
pentru abstractizarea apelurilor către S3.
Conform acestei documentații (https:// github .com/thephpleague/flysystem-bundle/blob/master/docs/4-using-lazy-adapter-to-switch-at-runtime.md), am configurat aplicația cu ceea ce ei numesc lazy adapter
pentru a putea să schimb implementarea în funcție de variabila de mediu.
Pînă aici, toate bune. Dacă sînt pe local, fișierele se încarcă în local, dacă sînt pe producție, fișiere se trimit către S3.
Bun.
Problema
Vreau să aduc cumva adresa acestor fișiere încărcate. Pentru asta, gîndesc eu, am nevoie tot de acea variabilă de mediu care decide în ce mediu sînt.
Apelul preferabil, aș vrea să arate în felul următor:
$this->storage->getAdapter()->url($gallery->getImageUrl());
sau, preferabil:
$this->storage->url($gallery->getImageUrl());
Metoda url(string: $path): string
aș vrea să-mi aducă:
- dacă sînt în mediu local, adresa către fișierul cerut: http:// local .dev/[path-to-file]/file.jpg
- dacă sînt în producție, adresa către fișierul de pe S3: https:// s3.[aws-region].amazonaws .com/[aws-bucket]/[path-to-file]/file.jpg
Ce am făcut eu
O clasă \App\Services\Storage
(un fel de Factory
, dar nu chiar).
Două adaptoare \App\Services\Adapter\Local
și \App\Services\Adapter\Aws
Cele două adaptoare implementează interfața \App\Storage\Adapter\StorageAdapterInterface
. Momentan interfața asta definește doar cum să arate metoda url(string: $path): string
, dar plănuiesc să mai adaug.
În \App\Services\Storage
, preiau din variabilele de mediu, valoarea care-mi spune pe ce mediu sînt, și în funcție de ea, creez o nouă instanță dintr-un adaptor (Local
sau Aws
):
services.yml
:
services:
App\Services\Storage\Storage:
arguments:
$adapter: '%env(APP_UPLOADS_SOURCE)%'
\App\Services\Storage
:
public function getAdapter(): StorageAdapterInterface
{
$class = 'App\\Services\\Storage\\Adapter\\'.$this->parseAdapterName();
if (!class_exists($class)) {
throw new \InvalidArgumentException("Storage adapter {$class} does not exist.");
}
$adapter = new $class();
return $adapter;
}
Treaba asta ar funcționa, dacă ar fi să rămînă implementarea așa cum este acum.
Concluzii
Problema este că la instanțierea adaptorului, $adapter = new $class();
, trebuie să trimit parametrii acelui adaptor, parametrii care diferă la Aws
față de Local
. Spre exemplu, pe local aș avea nevoie de informații de la Symfony, despre domeniu (URI), în timp ce pt. AWS aș avea nevoie de numele bucket-ului sau regiunea S3 pentru a forma acel URL corect.
Efectiv nu știu și nici nu am reușit să-mi dau seama cum să fac asta într-un mod care să nu polueze clasa \App\Services\Storage
cu informații de care nu are nevoie. Practic, am nevoie să injectez în \App\Services\Adapter\Local
și \App\Services\Adapter\Aws
acești parametrii (luați în mare parte din variabilele de mediu). Dar nici nu vreau să pun în \App\Services\Storage
if-uri sau switch-uri.
M-am documentat despre servicii tip Factory în Symfony de aici:
Dar nimic de acolo nu-mi explică cum să pasez alți parametri la un obiect, în funcție de o variabilă de mediu sau parametru. Mă gîndesc că acel adaptor ar trebui să poată să se construiască singur, cu toate dependințele de care are nevoie.
Voi ce ziceți? Gîndesc eu greșit? Drept e că nu am mai implementat un Factory
pînă acum.