Using Laravel And Vue To Create A Radically Different Content Management System
Having worked with many popular Content Management Systems (CMSs) over the years, I thought I would try my hand at creating my own, from scratch. Not just any CMS, but a CMS that throws the existing concept of what a CMS should be in the bin. This article serves as a brief overview of the core concept I have implemented and I hope to hear your views on what I’ve done in the comments below.
I’ve reached that stage in my programming career where I have gained a lot of experience building websites using different CMSs, languages, methodologies, frameworks and so on. For a long time now I’ve been experimenting with ways to get around common problems that seem to occur again and again when building and maintaining websites using popular CMSs of the day. About a year ago I decided to commit to creating a CMS that eliminates these problems by taking a radically different approach. In this article I detail the different approach I took and the various tools I employed.
A Different Approach
To begin with, I made the decision that this CMS will be built (at least initially) for use by people that are skilled and knowledgable when it comes to web development. Contentious perhaps, but with that stroke I’ve just removed a whole load of what I view as unnecessary complications that would cloud my judgement and would most likely lead to me writing a CMS that is just like every other CMS on the planet (that said, I’m not ruling out the system having the ability to provide a globally user-friendly backend at some stage, in fact if, when I reach the end of the project, this isn’t possible, I’ll consider the project a failure).
So, to be absolutely clear, I want to develop this CMS on the premise that the construction and maintenance of a website (in its entirety) built with this CMS, should rest squarely on a developer. No designers. Barely any client direction other than receiving content (no instructions on how to present the content). Just a developer, creating an entire website. In the same sense that you might pass an artist some kitchen rolls and glitter and ask them to make some artwork.
My (vague at this point) approach to making this viable was to create a system that is both component based like Polymer and Vuetify, and data orientated. I wanted to create an underlying schema that describes a component in a similar fashion to how HTML is used to describe an element on a page. I want a system to which I could say:
Display a nav bar.
And the navbar component will look for data in the schema and render itself based upon what it finds in the schema. It finds a taxonomy, it displays it. It finds some classes, it includes them. It’s opinionated, but you can override its opinions should you need to.
Next step, I needed to experiment and (hopefully) prototype this to prove to myself that it is a worthwhile concept.
Writing Components Using VueJs
I set about writing various components using VueJS. I’d learnt a trick that involves iterating over components using what the VueJS docs term a dynamic component.
v-for="(item, key) in items"
If you haven’t encountered this before, we’re iterating over an array of objects that each state a component type. This type relates to a component I have written, let’s say a menu. So
item contains the type, “navcomponent” and data that the nav component will want to render, such as menu items and perhaps classes that determine the styling. The whole data object gets passed as a prop
:opts . There, you have it.
Something else that I learnt about VueJS is that, when an object key doesn’t exist, VueJS simply doesn’t render the property. It doesn’t halt execution. This neat characteristic removes the need for the endless conditional statements and logic pathways you often find in CMSs.
These two things combine to allow for dynamic rendering of components that can mutate based upon the data made available to them.
Next, I needed to define a schema. I wanted every component to adhere to a standard organised structure. I had one field already: the component type. I would need to consider how the rest of the schema should be structured which is no easy task given that all the components would adhere to using the structure I decided upon. And what about storing the schema? If I was to store the data that defines the page against a route then it would lead to data coupling. I needed to decouple the data from the route so that I could use the same component on several pages without needing to update each individually. Simple fix: store the key against the route and construct the data assigned to a route when the route is requested.
After much deliberation, I came up with this concept:
User navigates to a route
Request sent to controller which queries “home” and returns the key(s) that relate to the data required for this route.
I loop the array of objects (containing only keys) returned from the query, replacing the key with the fully qualified object.
"title": "We are Stellify",
I pass this data to the view as JSON.
I feed the data to
<component> which will loop the array of objects rendering out the components they represent.
That was the general idea.
I set to work on making more components, experimenting, testing various scenarios. I figured that within a component block there’s no reason why you couldn’t use
<component> again within a component, in order to loop sub-components, if required. So I had what felt like a powerful tool for automating the rendering of content on a webpage with relative ease. Better still, I felt like I’d opened a whole raft of potential.
At this point I shifted my focus to how someone could build a site using this concept. What I did next was unplanned, I just found myself doing it. I coded VueJS components that would build data objects that adhered to my schema. I built a form builder component. It’s a visual builder but the end result is a simple, human readable scheme that defines a component and its associated data. I went on to build a general component builder using components from Bulma and (once I had discovered it) Buefy as a means of speeding up my prototyping. You select your component type and (depending on the type you chose) you’d be presented with various fields that relate to the type (you also dynamically add your own fields if you wish). The schema was loose in the sense that ultimately, the components would only use the data they are programmed to use. Missing properties wouldn’t break anything, additional properties would be ignored until the component was developed to look for and make use of them.
To reiterate, you choose your component type.
You build your component data.
You save your component.
You add your component key to a route.
You request the route.
And that is how you build a website using my CMS.
Making My Code Platform Based
The final task (“final” in the sense that once it was complete I had a working prototype) was to work out how to make this platform based. I already knew I wanted to use PHP/ Laravel on the server. Some quarters were advising I use a JS/node framework given that the “app” is predominantly JS based. I didn’t like this, I mean I love JS and people are doing very cool things with node but I don’t see it as language suited to server-side scripting. Plus, I was already familiar with Laravel. I knew that it offered major flexibility in relation to how you set up a project and I’d been researching how best to structure a Laravel project using packages so that you have a true platform, a codebase that acts as a layer upon which any type of website can exist. So I went ahead and set up a package, moved all the assets that were not “project specific” into the package and pushed it to a repository.
To cut a long story short, with the help of a Mexican wizard, I wrote a development and production Docker workflow. The production setup uses GitLab CI/CD to build and deploy the app using containers. The script pulls in the codebase as a package and the outer Laravel install contains all the files that are project specific such as environment files, css/ sass assets and so on. My next steps involve migrating to using Kubernetes, investigating how I could allow sites to be build via an API and trying to sell this idea to people like you, yes you, the person reading this article.
So have I written the perfect CMS? Probably not, but from my perspective I’ve certainly done something a bit different, albeit I haven’t exactly been a trailblazer. I’ve really just leveraged ideas, practises and technologies that are very much prevalent across the world of web development at the moment and I’ve done this in quite a methodical, structured way that looks to work very nicely. One thing I do feel pleased with myself about is how this setup hasn’t slammed any doors shut. I feel like I have all options still available should I want to use them. I’m still able to use all aspects of the Laravel PHP framework and ecosystem and I’m still able to pull in JS plugins without feeling like I’m hacking about with code to integrate third party code. Thanks to Sass variables sitting outside of the package containing the components, I can style websites built on top of the platform to make them look distinct from one another. The only thing I know I’ve broken for sure is the ability for SEO software to analyse the source code as prior to rendering all you will see on the page is the
<component>element (this could be fixed using pre-rendering but I’ll leave that to someone who cares about SEO software! Google can render and crawl the pages, job done!)
I didn’t write this article to give my verdict however, I wrote it for you, the reader, to give your verdict. So let me know what you think.
PS. I have a site built using the platform up on the WWW: https://stellifysoftware.co.uk/ check it out!