Puli is a new toolkit built on top of Composer that helps to manage and exchange resources like configuration files, images, CSS files, translation catalogs, and others. These are, you’ll agree, often difficult to maintain and share across projects.
Puli provides a framework-agnostic solution to this problem, keeping track of every resource location and avoiding the problems of using absolute or relative paths inside different systems.
How it works
- You map resources via CLI.
- A
puli.json
file keeps track of them. - You use a
ResourceRepository
instance to handle resources.
The Puli project also provides libraries for other functionalities like URL generation, or Twig support.
Install the Puli CLI
To use Puli, first you need to install the Puli CLI. It’s recommended to install it as a phar, which can be done by downloading the latest release from GitHub.
It can be placed in the /usr/local/bin
directory for global access.
Below there are command line steps for a quick installation of the 1.0.0-beta7 pre-release in Unix-like systems:
wget https://github.com/puli/cli/releases/download/1.0.0-beta7/puli.phar
chmod 755 puli.phar
sudo mv puli.phar /usr/local/bin
You can also install Puli as a Composer dependency in your project folder:
composer require --dev puli/cli:^1.0
or as a global Composer package with:
composer global require puli/cli:^1.0`
Once you have installed the CLI, you can use Puli in a Symfony Project, in a PHP Application or a Composer package. You can find more information on your specific project’s type in the getting started guide.
Project layout
It’s always recommended to have a neat directory project structure. The Puli docs reinforce this suggestion, adding a clear separation between the PHP code and the other non-PHP resources.
Below is a recommended directory layout example:
Mapping resources
Mapping single files or entire directories can be performed with the Puli map
command. For example, to map the res
directory’s content using myapp
as a resource name (Puli path), you can run:
puli.phar map /myapp res
You will notice that the map is stored in the puli.json
file generated inside the project folder.
It is now possible to reach all the mapped resources from PHP using the Puli path /myapp
.
The Puli CLI offers other useful commands to manage the mappings. Some of the most used are:
ls
, to list all the resources.tree
, to print the resource list tree.find
, to find a resource with some filter criteria, (i.e.puli find --name *.css
will find all the.css
files).map
with-u
or-d
arguments, to update/delete a map (ie:puli map -u myapp --add /new/folder
).
Another important feature is the ability to map resources in Composer packages. For example, to map the /view
directory inside a /vendor/author/packagename
directory you could use:
puli map /somepulipath @author/packagename:view
Pratical example
To show Puli in action, I have created an example project with Symfony: an image gallery. The demo displays all the images stored in an images_resource
folder.
Asset management
Before moving on, it’s worth pointing out a few things about the asset management:
- Symfony uses the
web
folder to store all the front controller files and the web assets (CSS, JS, images, other). Theweb
folder is, by default, the web application’s public access point. In other words, direct URL access to the other project directories is not possible. - The demo project contains one
AppBundle
with its CSS and views stored insrc/AppBundle/Resources
. - The Symfony asset management automatically copies every bundle’s resources into the public
web
folder.
You will see how asset management with Puli is more flexible and provides extra configuration options.
Installation of the demo
Check out the first version with:
git clone -b 1.0 https://github.com/niklongstone/symfony-gallery-demo.git
then enter the project’s root directory and run: composer install
.
The Symfony console will ask you to configure some parameters, but you can just press enter to accept the default values.
The two important parameters are:
images_resource
: the images folderimages_url
: the public images URL
Finally, in the project root, you can run app/console server:start
to run the server and see the app in the browser.
Mapping the project
To map all the resources in the demo project Bundle you can run:
puli.phar map /myapp/ src/AppBundle/Resources
With puli.phar tree
you will see something like:
The Controller
To use the mapped view in the Controller, you can change the DefaultController
as follows:
//old value: $this->template->render('AppBundle:Default:index.html.twig',
$this->template->render('/myapp/views/Default/index.html.twig',
You can reopen the project’s page to see that nothing has changed, because Puli perfectly called the mapped view.
The View
The next goal is to refactor the view using a Puli path for the CSS.
You can publish the resources of /myapp/public
into the public folder of the demo project, the web
directory. Publishing is essentially copying or symlinking the resources over.
Let’s do it step by step:
-
Create a server and link the public folder to it with:
puli.phar server --add localhost web
-
Register the Puli public path with:
puli.phar publish /myapp/public localhost /app
-
Install the resources with:
puli.phar publish --install
The command will create symlinks of the /myapp/public
elements into the /web/app
path.
Now, it’s time to change the CSS path in the index.html.twig
as follows:
<link href="{{ resource_url('/myapp/public/css/style.css') }}" rel="stylesheet">
At this point you can find all the progress you have made till now in the version 2.0 of the demo project:
git clone -b 2.0 https://github.com/niklongstone/symfony-gallery-demo.git
or
git checkout 2.0
if you already have the demo.
Don’t forget to run composer install
in the project’s folder, then puli.phar publish --install
.
URL generation
One of Puli’s advantages is to support different server resource locations. For example, if you want to move your resources to a different server like https://www.sitepoint.com/res/
you can run:
puli.phar server -u localhost --url-format https://www.sitepoint.com/res/%s
and every mapped URL will be updated automatically.
Another use case can be creating a simple cache invalidation system, appending to the query string something like %s?v1
.
Resource discovery
The demo project is quite flexible and allows us to change the images folder and URL. Puli provides a Discovery component which can add even more flexibility – for example in filtering specific resources.
The Discovery component manages the connection between resource providers and resource consumers. In other words, it can connect resources with services that will use them.
In the example, the controller can be a resource consumer, and the image folder can be a resource provider. To handle a resource with the Discovery component, you first define a resource type, then link it to a resource path.
In the example, we will create an app/image
resource type linked to the web/images
folder with a filter to get only JPEG images.
First, we need to map the web/images
folder with:
puli.phar map /myapp/images /web/images
then, you create a resource type:
puli.phar type --define mygallery/image
finally, you can bind the mapped folder with the jpg
query:
puli.phar bind /myapp/images/*.jpg mygallery/image
Now, you can refactor the DefaultController
as follows:
// The constructor needs the puli Discovery component instead of the images folder
public function __construct(EngineInterface $template, KeyValueStoreDiscovery $puliDiscovery)
{
$this->template = $template;
$this->puliDiscovery = $puliDiscovery;
}
// [...]
private function getImages()
{
$images = array();
//the discovery component will find the bind type with the filter
$bindings = $this->puliDiscovery->findByType('mygallery/image');
//the loop gets the resources image names
foreach ($bindings as $binding) {
foreach ($binding->getResources() as $resource) {
$images[] = $resource->getName();
}
}
return $images;
}
The AppBundle/Resource/config/service.xml
might be:
services:
app.default_controller:
class: AppBundle\Controller\DefaultController
arguments:
- @templating
- @puli.discovery
If you reopen the homepage, you should notice that only .png
images are missing, as expected.
To see the final version of the demo project, check out version 3.0 and run composer install
and puli publish --install
to see the correct result.
The Discovery component was built mainly to connect Composer packages and make this integration easy. You can find more information on The Discovery Component official page.
Final thoughts
Puli solves a real problem about resource management. The CLI is easy to use, and the Composer connection is one of the strongpoints of the project. Thanks to the author and all the contributors, there will soon be a stable release; although the beta works quite well.
You can find more information about Puli in the official documentation and share your personal thoughts in the comments below.
Nicola Pietroluongo is a software engineer with many years of experience, open source enthusiast, now creating and contributing to awesome PHP web projects.