About Sharp for Laravel — Part 1.

Refused title: Oh no! Another admin-panel CMS-like building package for Laravel.

Philippe Lonchampt
code16
8 min readFeb 11, 2018

--

There is a lot of packages that intend to manage content for websites, and/or to provide a useful dashboard for clients. Most of them can’t be really used outside of their initial frame, and even more of them aren’t maintained anymore, but still we can find some.

To name a few, Spatie, the Thousand and one Packages company, has one: https://github.com/spatie/blender. And there are of course some well-known products, https://octobercms.com, https://laravelvoyager.com, https://lavalite.org or https://statamic.com.

So why another one? Seriously, WHY?

Well, first, let’s say that the projects we build at Code 16, our little company, are very different from each other.

We have theaters (maillon.eu or festivalmusica.org) which need to share a lot of information, to manage ticketing configuration based on tricky rules, to follow orders and reservations, to answer questions and to send newsletters and notifications.

We have online shops (ambianceetstyles.com) which do all that and need to handle a lot of products, special offers, orders, clients, all this linked to CRM databases and emailing plateforms.

We have also book stores (quaidesbrumes.com or librairie-amateur.com) who want a simple tool to follow orders, write blog posts, add comments on API-filled books.

We also do more CMS-like (meaning data-centric) websites, like revue-radar.fr or coulon-architecte.fr.

All those projects are powered by Laravel, and all use Sharp for the admin section, and only Sharp: administrators can handle the whole system, meaning writing and publishing content, follow and manage orders, check comments, launch jobs, … in the same interface.

Okay, what’s Sharp?

Here’s how we present it: Sharp is a content management framework, a toolset which provides a clean API to build an admin section in a website, with some rules in mind. Here they are:

The public website should not have any knowledge of Sharp — it is a part of the system, not the center of it. In fact, removing Sharp should not have any effect on the project.

First rule is clear: Sharp is a package, added to the project. Not the opposite. And related to that: Sharp must handle data, and only data, and not how they are displayed or presented in the public section.

Sharp should not have any expectations from the persistence layer: MySQL is cool — but it’s not the perfect tool for every problem. And more important, the DB structure has nothing to do with Sharp.

It can be summarized as “Sharp is not Wordpress. At all”.

Content administrators should work with their data and terminology, not Sharp terms. If the project is about spaceships, space travels and pilots, why would the Sharp talk about articles, categories and tags?

The content administrators must work with a tool built for them.

Website developers should not have to work on the front-end development for the admin section.

Because life is complicated enough, Sharp takes care of all the responsive / CSS / JS stuff. The front code components were carefully built with the great Vue.js framework.

Sharp intends to provide a clean solution to the following needs:

  • create, update or delete any structured data of the project, handling validation and errors;
  • display, search, sort or filter data;
  • execute custom commands on one instance, a selection or all instances; and handle instances states (for a workflow for instance);
  • handle authorizations.

I think you need some pictures now. Or code. Something concrete.

First, basic stuff: Sharp is about Entities (structured data) displayed in Lists and created / updated in Forms.

Let’s pretend we have to manage handball players (yes, I took a sport example, and yes I choose this one on purpose because Americans are great at it 😈). Our application already has a Player model, and a players table. Let’s build a PlayerSharpList to display them:

  • buildListDataContainer() is for building the list colums. We choose to have three.
  • buildListConfig() is for… the list configuration. We start with nothing here.
  • We use buildListLayout() to tell how columns should be displayed, in a 12-based grid. There are some responsive options here, but it mostly this simple.
  • And finally getListData() must return the data for the list. Here we use Eloquent to retrieve all Players, and we transform them using Sharp API. The transformation is mostly a toArray() process, and can be handled as we want — Sharp only provide facultative helpers.

So this is simple code, and a first thing to notice is that Sharp is mostly this: entry points for functional code. There is some configuration, but the bigger part is code, and this mean that we can generally do anything we want in those spaces: grab data from a file, test session values, check a env variable, …

The result, in Sharp, is (after a db seed):

Simple code, simple result, right? Let’s work a bit to do 4 common things:

  • First add pagination:
  • Then some columns sorting:
  • Let’s say we decide that ratings should be displayed as stars (introducing custom transformers):
  • Finally, we want to be able to search on player or team name:

Note: we are using Font Awesome here because Sharp is leveraging it for its menu, but we could have done it in some other way.

Let’s see:

That’s better — and still, I only need a few lines of code. Notice that Players could have easily be gathered from an API call, or an XML file for instance: Eloquent is easy to use with Sharp, especially where it comes to updating, but not mandatory.

We can continue by adding filters on team or ratings, a state handler to quickly declare that a player is retired or injured, or an instance command to, for example, preview the player’s public page. But to keep this post readable in less than a day, let’s move to to the second huge part of Sharp: Forms.

Okay, and how do we actually update the data?

We write a Form. That’s another class.

  • buildFormFields() is for building the fields. There are many fields handled in Sharp (Wysiwyg, Markdown, Dates, Autocompletes, Selects, …). And a powerful List Field for, well, lists.
  • We use buildFormLayout() to organize fields, with the help of columns, tabs and fieldsets.
  • find() is for gathering initial data. We choose to use the same transformer methods we’ve seen for List—we can simply return an associative array here.
  • And finally update() and delete(), which are skipped for now.

Here’s the incredible result for this code:

Okay, that’s a huge screenshot for 3 fields. But it’s for consistency…

This is a really simple example, but I think you can feel the spirit: Forms are built via code using Sharp API (just like Lists). That grants a lot of control on how we do it, depending on functional needs.

Yes, but how do we ACTUALLY update the data?

Let’s get to it. There is a couple ways:

  • either you write some update code, based on what is sent back:

I’m using Laravel’s tap helper for brevity here: see here how this works if you’re not used to it.

  • Or… if you use Eloquent, just let Sharp handle it.

Wait… what is the point of this? Code is almost the same. Well, when using WithSharpFormEloquentUpdater trait, Sharp will handle all the update job, including relations (all of them: hasMany, belongsTo, belongsToMany, polymorphic ones, …). In our simple example, this has no effect, but in real life it’s a huge difference.

For really specific cases, you can easily tell the updater to ignore a data:

But in fact, for cases like this, it even easier and smarter to use custom transformation, just like for Lists:

Let’s call it a day

There is a lot more to review: form validation, global and specific authorizations, special creation case, and even more with multi-forms, the home dashboard, how to create menu, … And before all that, in a more realistic example, Team should be its own Entity, linked to Player via an autocomplete field(with local or remote data). And maybe we would like to handle attachments: a picture for the Player, a visual for the Team. All of this is for later posts.

Content management is a hard topic (and we are really aware of this: Sharp is not our first attempt to do this, nor our second, or our third. Or even fourth!), and we can’t review all of Sharp in this post. The point is to understand that this tool is meant to handle all kind of data, with a balance between writing a lot of code and having a good level of control.

It’s definitively working for us. Maybe it would for you: if you give it a try, please tell us what do you think of it, now it’s opened on the wild world of open-source.

Here’s part 2 of this series.

--

--

Philippe Lonchampt
code16

Developer and founder of Code 16, maintainer of Laravel Sharp.