02/07/2024

Laravel back & front : create a CMS from scratch with Filament PHP

Hey, i'm trying to get rid of wordpress, not so easy !! Filament PHP is a good candidate for building serious Content Management System, and I have started working on Repeater Fields, including editorJs field, image Field, and selector for choosing templates... Combined with Laravel Blade templating, this is a good prototype for a future CMS.

First let me introduce the project : a simple Blog / product website, currently powered by a wordpress farm with 20+ sites, which , incidentally, was hacked, requiring a few days work to migrate to a safe place. Thus decision was made to try something else, less popular, less prone to hacker intrusion, something you can switch off easily, some kind of may be a combination of private backend and front end headless solution.

Having experience with Laravel Nova and my own admin apps to deal with CRUD (Create Read Update Delete) or BREAD (Browser Read Add Delete) operations, I decided to give a go at Filament PHP, serious free alternative for Nova (leader from Laravel creator Taylor Otwell initiative) and Backpack (historical player) data managers. I don't mind paying a few hundred for Nova, but the minutes I gain from not having to activate a payment / license verification for each install, and instead getting up Filament with simple composer commands finally save me hours, including the fact that junior players from my team can also try Filament at home during the weekend and come back on monday with extended expertise.

Backend : Which Filament modules for wysiwyg and images ?

Filament comes with native Repeater Fields, that's a big argument for choosing this platform for backend. As I said before, I find Filament documentation not rich enough, even though searching finds solution for every problem. Getting EditorJs field to work for version 3 was not easy, we almost gave up. but well, it's here now, we have a single JSON field that can store any combination of editorJS / image / selector / whatever fields you want.

Speaking of images, I tried a few plugins for dealing with folders and stuff and can't remember them all. For now we stick to free stuff (remember your junior staff will try it at home), and Outer web Image library uploader seems to do the job, even though it does not come with folder management... Let's give it a go :

	Repeater::make('wave_content')->schema([
         EditorJs::make('wave_text')->label('Text') ->columnSpan(1) ,
	ImageLibraryPicker::make('wave_image')->label('Image') ->columnSpan(1) ,
        Select::make('wave_template')  ->options([
                                "2col1"=>"2 columns (image / text ) ",
                                "2col2"=>"2 columns (text  / image) ",
                                "header"=>"Image hero  ",
                                "card"=>"Image / text"
                            ])
	]) ->columns(2)

Front End : How do I deal with this

on the frond end, Filament has it all sorted for you. All you have to do is decode the JSON from the repeater, and also the JSON from each editorJS. Some big work is done in pure PHP controller, and some templating is better living in blade includes.

Controller : decode editorJS

on the controller you have full access to PHP array manipulation : use this to parse editor JS content, stored as JSON for each repeater instance. Decode it to HTML using Setono's parser.

    public function blogEntry(\App\Models\MajuskuleBlog $blog)
    {
        $arr_content_rendered=[] ;
        foreach($blog->wave_content as $k=>$v)
        {
           // $blog->content_rendered[$k]=  Array();
            $arr_content_rendered[$k]["wave_template"]= $v["wave_template"];
            $arr_content_rendered[$k]["wave_image_renderer"]= $v["wave_image"];
            $arr_content_rendered[$k]["wave_text_renderer"]= $this->renderEditorJs($v["wave_text"]);
        }
        $blog->arr_content_rendered=$arr_content_rendered;
        return view('blog_entry', [ 'blog' => $blog,  ]);
    }
    private function renderEditorJs($json_data)
    {
        $parser = new Parser();
        $parserResult = $parser->parse(json_encode($json_data));

        $renderer = new Renderer();
        $renderer->add(new DelimiterBlockRenderer());
        $renderer->add(new HeaderBlockRenderer());
        $renderer->add(new ImageBlockRenderer());
        $renderer->add(new ListBlockRenderer());
        $renderer->add(new ParagraphBlockRenderer());
        $renderer->add(new RawBlockRenderer());

        return  $renderer->render($parserResult);
    }

Views / blade includes : choose model

once you have your HTML ready to deliver, make templates that you include dynamically.

@if ($blog->arr_content_rendered )
@foreach($blog->arr_content_rendered as $blogContentElement)
                @include('blocs/' .$blogContentElement["wave_template"])
 @endforeach

And there you go. Want a sample template ? Have not finished that yet, I'm working on this : Many basic bloc template to come on laravel blade system !! If you have no idea what' im talking about you might a look at a few existing blade libraries based on tailwind, my current preferred style toolkit.

To top