Table of Contents
Table of Contents
Traditionally a PHP application deals directly with all kinds of
files. Realizing a file upload is usually an excessive task because you need
to create a proper upload form, deal with deciphering the
$_FILES superglobal and move the uploaded file from the
temporary location to a safer place. You also need to analyze the content
(is it safe?), control web access and ultimately delete the file when it's
not needed anymore.
FLOW3 relieves you of this hassle and lets you deal with simple
Resource objects instead. File uploads are handled
automatically, enforcing the restrictions which were configured by means of
validation rules. The publishing mechanism was designed to support a wide
range of scenarios, starting from simple publication to the local file
system up to fine grained access control and distribution to one or more
content delivery networks. This all works without any further ado by you,
the application developer.
FLOW3 packages may provide any amount of static resources. They might be images, stylesheets, javascripts, templates or any other file which is used within the application or published to the web. Static resources may either be public or private:
public resources are automatically mirrored to the public web directory and are publicly accessible without any restrictions (provided you know the filename)
private resources are not published by default. They can either be used internally (e.g. as templates) or published with certain access restrictions
Whether a static package resource is public or private is determined
by its parent directory. For a package Foo the public
resources reside in a folder called
Foo/Resources/Public/ while the private resources are
stored in Foo/Resources/Private/. The directory
structure below Public and
Private is up to you and will be cloned to the web
resources folder.
Data which was uploaded by a user or generated by your application
is called a persistent resource. Although these
resources are usually stored as files, they are never referred to by their
path and filename directly but are represented by
Resource objects.
It is important to completely ignore the fact that resources are stored as files somewhere in FLOW3's directory structure – you should only deal with resource objects.
New persistent resources can be created by either importing or
uploading a file. In either case the result is a new
Resource object which can be attached to any other
object. A resource exists as long as the Resource
object is connected to another entity or value object which is persisted.
If a resource is not attached to any other persisted object, it will be
cleaned up at the end of the script run.
Importing resources is one way to create a new resource object. The
ResourceManager provides a simple API method for
this purpose:
Example 7.1. Importing a new resource
class ImageController { /** * @inject * @var \F3\FLOW3\Resource\ResourceManager */ protected $resourceManager; // ... more code here ... /** * Imports an image * * @param string $imagePathAndFilename * @return void */ public function importImageAction($imagePathAndFilename) { $newResource = $this->resourceManager->importResource($imagePathAndFilename); $newImage = $this->objectFactory->create('F3\MyPackage\Domain\Model\Image'); $newImage->setOriginalResource($newResource); $this->imageRepository->add($newImage); } }
The ImageController in our example provides a
method to import a new image. Because an image consists of more than just
the image file (we need a title, caption, generate a thumbnail, ...) we
created a whole new model representing an image. The imported resource is
considered as the "original resource" of the image and the
Image model could easily provide a "thumbnail
resource" for a smaller version of the original.
This is what happens in detail while executing the
importImageAction method:
The URI (in our case an absolute path and filename) is passed to
the importResource method which analyzes the
file found at that location.
The file is imported into FLOW3's persistent resources storage using the sha1 hash over the file content as its filename. If a file with exactly the same content is imported it will reuse the already stored resource.
The Resource Manager returns a new
Resource object which refers to the newly
imported file.
A new Image object is created and the
resource is attached to it.
The image is added to the
ImageRepository. Only from now on the new image
and the related resource will be persisted. If we omitted that step,
the image, the resource and in the end the imported file would be
discarded at the end of the script run.
In order to delete a resource just disconnect the resource object
from the persisted object, for example by unsetting
originalResource in the Image
object.
The second way to create new resources is uploading them via a POST
request. FLOW3's MVC framework detects incoming file uploads and
automatically converts them into Resource objects.
In order to persist an uploaded resource you only need to persist the
resulting object.
Consider the following Fluid template:
<f:form method="post" action="create" object="{newImage}" name="newImage" enctype="multipart/form-data">
<f:form.textbox property="image.title" value="My image title" />
<f:form.upload property="image.originalResource" />
<f:form.submit value="Submit new image"/>
</f:form>This form allows for submitting a new image which consists of an image title and the image resource (e.g. a JPEG file). The following controller can handle the submission of the above form:
class ImageController { /** * @inject * @var \F3\FLOW3\Resource\ResourceManager */ protected $resourceManager; // ... more code here ... /** * Creates a new image * * @param \F3\MyPacakge\Domain\Model\Image $newImage The new image * @return void */ public function createAction(\F3\MyPacakge\Domain\Model\Image $newImage) { $this->imageRepository->add($newImage); $this->forward('index'); } }
Provided that the Image class has a
title and a originalResource
property and that they are accessible through
setTitle and
setOriginalResource respectively the above code
will work just as expected.
By default static resources (usually provided by packages) are published to the web directory on the first script run and whenever packages are activated or deactivated. If resource files are added, removed or changed after the first run, they won't be published again. This behavior is desired in a production context where it would be to time intensive to check for updated resources on every run.
In a development context however, you'll gladly sacrifice some microseconds for the convenience of automatically updated resource files. This can be achieved by setting "resource: publishing: detectPackageResourceChanges" to yes – which is already the case in the Development context settings in FLOW3's standard distribution.
Published static resources can be used in Fluid templates via the built-in resource view helper:
<img src="{f:uri.resource(path: 'Images/Icons/FooIcon.png', package: 'MyPackage')}" />Note that the package parameter is optional
and defaults to the package containing the currently active
controller.
Persistent resources are published on demand because FLOW3 cannot know which resources are meant to be public and which ones are to kept private. The trigger for publishing persistent resources is the generation of its public web URI. A very common way to do that is displaying a resource in a Fluid template:
<img src="{f:uri.resource(resource: image.originalResource)}" />The resource view helper (f:uri.resource()) will ask
the ResourcePublisher for the web URI of the
resource stored in image.originalResource. The
publisher checks if the given resource has already been published and if
not publishes it right away.
A published persistent resource is accessible through a web URI
like
http://example.local/_Resources/Persistent/107bed85ba5e9bae0edbae879bbc2c26d72033ab.jpg.
One advantage of using the sha1 hash of the resource content as a
filename is that once the resource changes it gets a new filename and is
displayed correctly regardless of the cache settings in the user's web
browser. Search engines on the other hand prefer more meaningful
filenames. For these cases the resource view helper allows for defining
a speaking title for a resource URI:
<img src="{f:uri.resource(resource: image.originalResource, title: image.title)}" />A URI produced by the above template would look like this:
http://example.local/_Resources/Persistent/107bed85ba5e9bae0edbae879bbc2c26d72033ab/my-speaking-title.jpg.
You can define as many titles for each resource as you want – the resulting file is always the same, identified by the sha1 hash.
Publishing resources basically means copying files from a private location to the public web directory. Creating copies however comes with a little speed penalty and in some cases the size of duplicated resources can become an issue.
If your operating system supports symbolic links, you can speed up
the publication process by telling FLOW3 to create symlinks instead of
copies. This can be achieved through some setting in FLOW3's
Settings.yaml:
FLOW3: resource: publishing: fileSystem: # Strategy for mirroring files: Either "copy" or "link" mirrorMode: link
Static resources are often used by packages internally. Typical use
cases are templates, XML, YAML or other data files and images for further
processing. You might be tempted to refer to these files by using one of
the FLOW3_PATH_* constants or by creating a path
relative to your package. A much better and more convenient way is using
FLOW3's built-in stream package resources wrapper.
The following example reads the content of the file
MyPackage/Resources/Private/Templates/SomeTemplate.html
into a variable:
Example 7.2. Using the Package Resource Stream Wrapper
$template = file_get_contents('package://MyPackage/Private/Templates/SomeTemplate.html');You are encouraged to use this stream wrapper wherever you need to access a static package resource in your PHP code.