Oryzone / OryzoneMediaStorageBundle

Bundle to abstract the process of storing and retrieving media files
8 stars 11 forks source link

Examples are needed for 2.0 #25

Open fesja opened 11 years ago

fesja commented 11 years ago

Hi,

I've been looking several libraries and this is the one that I liked more. But after installing it, I'm not sure how to add pictures to my models. I could look through the code, but I'm sure you have some working examples of:

Hope you can share some code this weekend so I can code it :). Once I have become familiar with the code, I can help you with the docs & improve bugs!

thanks!

javier

lmammino commented 11 years ago

Hi @fesja, I'm really happy you're interested in using MediaStorageBundle v2.0. Before giving you a quick demonstration on how to use it I want to advice you that this release is still unstable and poorly tested, so use it at your own risk. Anyway if you still want to use I'm free to suggestions and improvements, so feel free to open issues and submit pull requests if needed.

Ok let's go.

Main Concepts

First of all there are few concept about the architecture that must be pointed out for the sake of a good understanding. OryzoneMediaStorageBundle revolves around 6 foundamental elements that must be composed togheter: Provider, Filesystem, Cdn, Naming Strategy, Variant, and ultimately Context. Each of these parts has been thought to be fully extendable and customizable even if the bundle offers some defaults that should be sufficient in most cases.

Provider

A provider is a way to abstract the logic behind a certain type of media. The bundle offers a series of default providers to store the most common media types (and their metadata): simple files (no processing), images (with possibility to crop and convert the source file format), external videos (actually only youtube and vimeo are supported).

Filesystem

A thin abstraction built upon Gaufrette that is used to configure where your files will be stored.

Cdn

Allows you to define the logic behind the public URL of your files. By default the bundle offers 2 different Cdn strategies:

Naming Strategy

Contains the logic behind your file names. Which name will be have a picture uploaded by a user? Naming Strategy decides it! By default the bundle offers a naming strategy that generates names like the following: avatar-user-loige-5345gf54324_sXS but you can obviously write your custom naming strategies if you like.

Variant

Each stored media can have different variants. Imagine you need to store an image and need different variants to be generated from the original file (you may want a scaled version, a cropped one and a square preview). While your media remains one (a single entry in your database) you will have different files associated to it (one for each variant). Variants can be easily defined in the configuration as a tree and are processed accordingly. The following picture shows a possible scenario: Variants tree example In this scenario your would be able give a source image file to the media storage library (e.g. by upload) and it will generates the DEFAULT variant, so it will generates the PREVIEW and THUMBNAIL variants by starting from the previously generated DEFAULT variant. Obviously the tree is not limited to one-level nesting, you can have more nested variants if needed.

Context

Contexts are like glue: a context allows you to put things together for a specific need. For example suppose you want to store users avatar image in your website: you will need to define an avatar context. In the context you must specify which provider handles the files (in this case you can use the image provider), which variants will be generated for each avatar, the naming strategy you want to use to name all the generated files, which filesystem and which Cdn to use to store and locate the files. Obviously you can create as much contexts as you need (avatar, uploaded pictures, downloadable files, youtube attached videos and so on...) the only limitation is that each context can use a single provider.

Now you should have got a idea of the whole structure let's go with the configuration.

Configuration

This complex structure should not be defined manually by instancing and connecting all related class but can be defined simply within the Symfony configuration. I will provide a sample configuration to go straight to the point. I suggest you to create a separate configuration file (e.g. mediastorage.yml) and attach it to your main config.yml this way:

# config.yml
imports:
    - { resource: mediastorage.yml }

Scenario: suppose you are building a website were registered user can attach youtube videos the like. So each user have an avatar and he can post youtube videos, we need 2 separate contexts: avatar and video. You would like to store the files on the local filesystem (you will not use any external storage like amazon S3 and need to configure a local CDN to locate your files).

Follows a sample configuration:

# mediastorage.yml
knp_gaufrette:
    adapters:
        avatar_adapter:
                local:
                        directory: '%kernel.root_dir%/../web/img/avatar'
        video_adapter:
                local:
                        directory: '%kernel.root_dir%/../web/img/video'
    filesystems:
        avatar_filesystem:
            adapter:    avatar_adapter
        video_filesystem:
            adapter:    video_adapter

oryzone_media_storage:
    db_driver: orm #to use doctrine orm drivers (you can also use 'mongodb')
    cdns:
        avatar_cdn:
                local: { path: '/img/avatar/' }
        video_cdn:
                local: { path: '/img/video/' }
    contexts:
        avatar:
            provider: image
            filesystem: avatar_filesystem
            cdn: avatar_cdn
            namingStrategy: slugged # the default (and actually the only one) available naming strategy
            variants: #defines the variants tree for the avatar images
                big:
                    process: { width: 400, resize: proportional, format: jpg, quality: 90 }
                small:
                    process: { width: 150, resize: proportional, format: jpg, quality: 90 }
                    parent: big
                square:
                    process: { width: 100, height: 100, resize: crop, format: jpg, quality: 80 }
                    parent: big
        video:
            provider: youtube
            filesystem: video_filesystem
            cdn: video_cdn
            variants: #variants are needed here because the youtube provider will download the video preview files and may process them generating different variants
                proportional:
                    process: { width: 500, resize: proportional, format: jpg, quality: 90 }
                square:
                    process: { width: 220, height: 220, resize: crop, format: jpg, quality: 80 }
                    parent: big

The first part of the configuration (knp_gaufrette) is related to gaufrette (please read the KnpGaufretteBundle documentations if you need more information about it).

Note: Obviously you can write your configuration in XML if you prefer (but I haven't tested it still).

Define your models

The OryzoneMediaStorageBundle actually supports Doctrine2 as data persistance mechanism and allows you to use Doctrine ORM and MongoDB.

The bundle offers two base abstract Media classes that you can extend to define your models. The only missing feature is the id, as you may want to handle ids in your own way (auto-increment, auto-generation, manual insertion). So you need to implement the method getId() in your concrete implementations.

For Doctrine Orm

Define your entity by extending the \Oryzone\Bundle\MediaStorageBundle\Entity\Media class.

Example:

<?php

namespace Acme\Bundle\DemoBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * Acme\Bundle\DemoBundle\Entity\Media
 *
 * @ORM\Table()
 * @ORM\Entity()
 */
class Media extends BaseMedia
{
    /**
     * @var integer $id
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * {@inheritDoc}
     */
    public function getId()
    {
        return $this->id;
    }
}

For Doctrine MongoDB

Define your document by extending the \Oryzone\Bundle\MediaStorageBundle\Document\Media class.

Create, Update and delete a media

That's quite enough with configuration, let's get coding now!

Store a new media

Here's a quick example on how to create (store) a new Media in your controller code:

<?php
// ... inside some action method of some controller

// Store an avatar (and all its variants)
$image = __DIR__ . '/../some/image/file.jpg';
$avatar = new \Acme\Bundle\DemoBundle\Entity\Media($image, 'avatar');
$avatar->setName('sample avatar');
$this->get('media_storage')->store($avatar);
// if you don't need the original file anymore you can delete it (you will now use generated variants)
@unlink($image);

As you can see you will not need to call doctrine directly (it's done out of the box).

Update a media

Suppose you want to change the image and the title of an already stored avatar.

<?php
// ... inside some action method of some controller

$newImage = __DIR__ . '/../some/new/image/file.jpg';

//retrieve your avatar instance someway with doctrine
$avatar = $this->getDoctrine()->getManager('AcmeDemoBundle:Media')->findOneById($someId) ;
$avatar->setContent($newImage);
$avatar->setName('new sample avatar');
$this->get('media_storage')->update($avatar);

@unlink($newImage);

Remove a media

Suppose you want to delete an avatar.

<?php
// ... inside some action method of some controller

//retrieve your avatar instance someway with doctrine
$avatar = $this->getDoctrine()->getManager('AcmeDemoBundle:Media')->findOneById($someId) ;
$this->get('media_storage')->remove($avatar);

Note: by deleting a media all it's previously generated files (variants) will be deleted from the filesystem.

Display your Media in the view

Ok you know how to create, edit and remove medias! Cool! Now you need to show them in your view.

Retrieve the url

Sometimes to show a media is sufficient to know its URL. If you are inside a controller you can obtain the url of a media this way:

// ... inside some action method of some controller

//retrieve your avatar instance someway with doctrine
$avatar = $this->getDoctrine()->getManager('AcmeDemoBundle:Media')->findOneById($someId) ;
$url = $this->get('media_storage')->getUrl($avatar, 'variantName');

Obviously each variant file has its own url so you need to pass the name of the variant you want to use as second argument to the getUrl method.

If you're using twig you can use the filter mediaUrl to obtain the url of a given media variant file. Example:

{# avatar is a Media instance passed to the template #}

<img class="avatar" src="{{ avatar|mediaUrl('variantName') }}"/>

Render the media

Rendering is an advanced function that may speed up the proper rendering of certain media files. Each provider has its own render method specialized to construct the html code for its media type: the image provider is specialized to render img tags, the youtube provider will render the youtube embed code and so on.

If you need to render the html of an image you can user the render method:

// ... inside some action method of some controller

//retrieve your avatar instance someway with doctrine
$avatar = $this->getDoctrine()->getManager('AcmeDemoBundle:Media')->findOneById($someId) ;
$avatarHTML = $this->get('media_storage')->render($avatar, 'variantName');

If you need to do it within a Twig template (best choice) you can use the mediaRender filter:

{# avatar is a Media instance passed to the template #}

<div class="user-avatar">
    {{ avatar|mediaRender('variantName') }}
</div>

This will generate an img tag with proper width, height, and alt attributes.

lmammino commented 11 years ago

Move this here https://github.com/Oryzone/OryzoneMediaStorageBundle/blob/2.0/Resources/doc/index.rst so anyone can contribute to the documentation.