app-verk/google-cloud-storage-media-bundle
Install the bundle:
composer require app-verk/google-cloud-storage-media-bundle
Enable the bundle in config/bundles.php:
AppVerk\GoogleCloudStorageMediaBundle\GoogleCloudStorageMediaBundle::class => ['all' => true],
Create a Media entity extending BaseMedia:
// src/Entity/Media.php
namespace App\Entity;
use AppVerk\GoogleCloudStorageMediaBundle\Entity\Media as BaseMedia;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
class Media extends BaseMedia {}
Configure the bundle in config/packages/google_cloud_storage_media.yaml:
google_cloud_storage_media:
namer: "AppVerk\\GoogleCloudStorageMediaBundle\\Namer\\DefaultNamer"
filesystem: "default.storage"
filesystem_url_retriever: 'AppVerk\GoogleCloudStorageMediaBundle\Flysystem\Retriever\GoogleObjectUrlRetriever'
entities:
media_class: App\Entity\Media
gcs:
project_id: "your-project-id"
bucket_id: "your-bucket-name"
key_file_path: "%kernel.project_dir%/path/to/keyfile.json"
allowed_mime_types: ["image/jpeg", "image/png"]
Configure Flysystem in config/packages/flysystem.yaml:
flysystem:
storages:
default.storage:
adapter: 'gcloud'
public: true
options:
client: 'gcloud_client_service'
bucket: 'your-bucket-name'
Configure the GCS client in config/services.yaml:
services:
gcloud_client_service:
class: Google\Cloud\Storage\StorageClient
arguments:
- projectId: "%google_cloud_storage_media.gcs.project_id%"
- keyFilePath: "%google_cloud_storage_media.gcs.key_file_path%"
Update the database schema:
php bin/console doctrine:schema:update --force
Add Dropzone.js and CSS to your admin panel:
<link rel="stylesheet" href="{{ asset('bundles/googlecloudstoragemedia/css/dropzone.min.css') }}" />
<script src="{{ asset('bundles/googlecloudstoragemedia/js/dropzone.min.js') }}"></script>
Add Twig form theme in config/packages/twig.yaml:
twig:
form_themes:
- '@GoogleCloudStorageMedia/form/fields.html.twig'
Register routes in config/routes.yaml:
media:
resource: '@GoogleCloudStorageMediaBundle/Controller/'
type: attribute
Create a form type to upload media:
// src/Form/Type/PostType.php
namespace App\Form\Type;
use AppVerk\GoogleCloudStorageMediaBundle\Form\Type\MediaType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class PostType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder->add('image', MediaType::class);
}
}
Render the form in Twig and display the uploaded media:
{{ form_start(form) }}
{{ form_widget(form) }}
<button type="submit">Submit</button>
{{ form_end(form) }}
<img src="{{ post.image|media }}" />
Form Submission:
MediaType in your form to handle file uploads.allowed_mime_types and max_file_size.Entity Management:
Media entity is saved to the database with metadata (filename, mime type, size, etc.).Retrieving Files:
|media to generate the URL for the uploaded file:
<img src="{{ mediaObject|media }}" />
Custom Validation Groups:
google_cloud_storage_media.yaml:
google_cloud_storage_media:
groups:
documents:
allowed_mime_types: ["application/pdf", "application/docx"]
max_file_size: 10000000
$builder->add('document', MediaType::class, ['group' => 'documents']);
Streaming and Reading Files:
StorageService to stream or read files:
use AppVerk\GoogleCloudStorageMediaBundle\Service\v2\StorageService;
class SomeService {
public function __construct(private StorageService $storageService) {}
public function streamFile(Media $media) {
return $this->storageService->readStream($media);
}
}
Custom Naming Strategy:
NamerInterface to customize how files are named in GCS:
use AppVerk\GoogleCloudStorageMediaBundle\Namer\NamerInterface;
class CustomNamer implements NamerInterface {
public function generateName(Media $media): string {
return 'custom_prefix_' . uniqid() . '.' . $media->getMimeType();
}
}
google_cloud_storage_media.yaml:
google_cloud_storage_media:
namer: "App\\Namer\\CustomNamer"
Event Listeners:
MediaUploadEvent) to perform custom actions post-upload:
use AppVerk\GoogleCloudStorageMediaBundle\Event\MediaUploadEvent;
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
class MediaUploadListener {
#[AsEventListener(event: MediaUploadEvent::class, method: 'onMediaUpload')]
public function onMediaUpload(MediaUploadEvent $event) {
// Custom logic after upload
}
}
GoogleObjectUrlRetriever for signed URLs or LocalObjectUrlRetriever for public URLs.Configuration Errors:
filesystem_url_retriever or using an incorrect retriever (e.g., LocalObjectUrlRetriever for GCS).filesystem_url_retriever matches your storage setup (use GoogleObjectUrlRetriever for GCS):
google_cloud_storage_media:
filesystem_url_retriever: 'AppVerk\GoogleCloudStorageMediaBundle\Flysystem\Retriever\GoogleObjectUrlRetriever'
Key File Path:
key_file_path in google_cloud_storage_media.yaml or services.yaml causing authentication failures.google_cloud_storage_media:
gcs:
key_file_path: "%kernel.project_dir%/config/gcs-key.json"
Mime Type Validation:
allowed_mime_types.use Symfony\Component\Validator\Constraints\File;
$builder->add('file', MediaType::class, [
'constraints' => [
new File([
'maxSize' => '10M',
'mimeTypes' => ['image/jpeg', 'image/png'],
'mimeTypesMessage' => 'Please upload a valid image (JPEG or PNG).',
])
]
]);
Flysystem Adapter:
flysystem.yaml and the service is autowired:
flysystem:
storages:
default.storage:
adapter: 'gcs' # Not 'gcloud'; use the correct adapter name
Database Schema:
How can I help you explore Laravel packages today?