Friday, March 24, 2023
Learning Code
  • Home
  • JavaScript
  • Java
  • Python
  • Swift
  • C++
  • C#
No Result
View All Result
  • Home
  • JavaScript
  • Java
  • Python
  • Swift
  • C++
  • C#
No Result
View All Result
Learning Code
No Result
View All Result
Home Swift

How to build a Feather CMS module?

learningcode_x1mckf by learningcode_x1mckf
September 20, 2022
in Swift
0
How to build a Feather CMS module?
74
SHARES
1.2k
VIEWS
Share on FacebookShare on Twitter


2020/08/07

On this tutorial I will present you tips on how to create a customized consumer module with an admin interface for Feather utilizing Swift 5 and Vapor 4.

Vapor

Module technology utilizing Swift templates

There’s an open supply template based generator tool for Swift that I’ve created, as a result of I wanted one thing to shortly arrange each VIPER and Feather modules. We’re going to use this generator to start out constructing our customized Feather module. You may set up Swift template by the command line:


git clone https://github.com/BinaryBirds/swift-template.git
cd swift-template
make set up


Now we simply want a starter template, fortuitously there’s a template accessible on GitHub that you need to use for producing modules which are suitable with the most recent model of Feather CMS. 🪶

We’re going to set up this template with the next command:



swift template set up https://github.com/feathercms/feather-module-template -g


Now we will bootstrap our customized module through the next command:


swift template generate MyModule --use feather-module --output ~/


You may alter the the title of the module, use an different template (have to be put in domestically or globally) and specify the output listing the place you need to save the module information.



Constructing a information module for Feather CMS

In Feather CMS you’ll be able to constructing a function wealthy module in just some minutes. That is proper, I will present you tips on how to make one utilizing Swift template and the Feather module template starter package. To begin with you may must seize Feather CMS from GitHub and generate a brand new module utilizing the generator.


git clone https://github.com/feathercms/feather/
cd feather
swift template generate Information -u feather-module -o ./Sources/Feather/Modules
open Package deal.swift


Replace your Swift package deal dependencies. You should utilize the Swift Package Manager and the command line (swift package deal replace) in case you are constructing the server with out Xcode. Alternatively you’ll be able to open the package deal manifest file and wait till Xcode resolves the dependencies. 📦


Earlier than we run the app, just remember to have created a neighborhood .env or .env.growth file that Feather can use to run the server.


BASE_URL="http://localhost:8080"


BASE_PATH="/path/to/feather/"


When utilizing Xcode, please double verify that you have set a custom working directory. ⚠️

Time to allow our newly created module, open to the primary.swift file and append the NewsBuilder() occasion to the module configuration array. It will allow the pattern information module. Now if you happen to run Feather, the brand new module ought to work by default, however earlier than we really check out all the pieces we’re going to alter the generated information module supply just a bit bit. 🔨



Mannequin definition

Let’s begin by altering the mannequin definition for our information entries. It will enable us to retailer information objects within the persistent database utilizing the underlying Fluent framework. The generated information module will include a NewsModel, we simply want to increase this mannequin with just a few extra fields.


import FeatherCore

last class NewsModel: ViperModel 
    typealias Module = NewsModule

    static let title = "information"

    struct FieldKeys 
        static var title: FieldKey  "title" 
        static var imageKey: FieldKey  "image_key" 
        static var excerpt: FieldKey  "excerpt" 
        static var content material: FieldKey  "content material" 
    

    

    @ID() var id: UUID?
    @Area(key: FieldKeys.title) var title: String
    @Area(key: FieldKeys.imageKey) var imageKey: String
    @Area(key: FieldKeys.excerpt) var excerpt: String
    @Area(key: FieldKeys.content material) var content material: String

    init()  

    init(id: UUID? = nil,
         title: String,
         imageKey: String,
         excerpt: String,
         content material: String)
    
        self.id = id
        self.title = title
        self.imageKey = imageKey
        self.excerpt = excerpt
        self.content material = content material
    


We outlined our Fluent database mannequin with the assistance of Swift property wrappers (@ID, @Area). They are going to enable Fluent to learn and write columns within the represented database desk, so we do not have to jot down SQL queries, however we will entry the entries by a a lot larger degree (ORM) abstraction layer. Fairly commonplace Vapor and Fluent stuff these days. 🙃

The id is a singular identifier, we will save the information title as a String, the imageKey is a particular property for saving picture URLs and the excerpt goes to be a brief “sneak-peak” of the whole content material. Now we simply have to jot down a migration script, as a result of in Vapor now we have to create or replace our database tables earlier than we might use the mannequin.

import Vapor
import Fluent

struct NewsMigration_v1_0_0: Migration 

    func put together(on db: Database) -> EventLoopFuture<Void> 
        db.schema(NewsModel.schema)
            .id()
            .discipline(NewsModel.FieldKeys.title, .string, .required)
            .discipline(NewsModel.FieldKeys.imageKey, .string, .required)
            .discipline(NewsModel.FieldKeys.excerpt, .string, .required)
            .discipline(NewsModel.FieldKeys.content material, .string, .required)
            .create()
    

    func revert(on db: Database) -> EventLoopFuture<Void> 
        db.schema(NewsModel.schema).delete()
    

This migration script will create the required fields contained in the information desk and if needed we will revert the method by deleting the whole desk.


Metadata in Feather CMS

In Feather CMS all the pieces that may be publicly accessed by the site must have an related metadata object. This metadata object is accountable for managing the general public url (slug) and visibility of the referenced entity, it additionally shops many extra Website positioning associated particulars.

Something can change into a metadata reference, we simply must implement a particular protocol on the item that we need to use as a frontend content material, plus now we have to setup a customized middleware so as to feed the metadata mannequin with some fundamental details about the refenreced object.

The generated template offers a default metadata represantation for the pattern mannequin, we simply have to increase the NewsModel+Metadata.swift file with the brand new fields that we added to our mannequin. This fashion our referenced metadata can know much more data in regards to the information feed merchandise.


import FeatherCore

extension NewsModel: MetadataRepresentable 

    var metadata: Metadata 
        .init(slug: Self.title + "https://theswiftdev.com/" + title.slugify(),
              title: title,
              excerpt: excerpt,
              imageKey: imageKey)
    

This MetadataRepresentable protocol is used after we save a information mannequin, Feather will create an related Metadata object with the returned title, excerpt and imageKey values. This connection works robotically if you happen to register a database middleware within the boot perform of your module file.


func boot(_ app: Software) throws 
    
    app.databases.middleware.use(MetadataModelMiddleware<NewsModel>())

    

Utilizing the metadata API is a good way to have good Website positioning-friendly public pages in your web site backed by your individual enterprise fashions with out pondering an excessive amount of in regards to the underlying knowledge construction.


Enter kinds

The default template additionally provides us the flexibility to handle the pattern mannequin by utilizing the CMS. We’ve to increase this performance a bit, as a result of we have added some further fields.

The LeafRepresentable protocol is a part of the Leaf framework, it permits us to render fashions utilizing the template engine. We’ve so as to add our personal properties contained in the NewsModel+View.swift file.

import FeatherCore

extension NewsModel: LeafDataRepresentable 

    var leafData: LeafData 
        .dictionary([
            "id": id,
            "title": title,
            "imageKey": imageKey,
            "excerpt": excerpt,
            "content": content,
        ])
    

This modification will enable us to listing, create, replace or view our mannequin with all of the accessible fields utilizing the Content material Administration System. The generated template provides us all the CRUD operations free of charge, however the interface solely works with the title discipline, so now we have so as to add the opposite newly created properties if we wish to have the ability to utterly handle our mannequin.


The ModelForm protocol permits us to supply edit (create, replace) performance for a given mannequin by the CMS. The shape has to outline the fields that you need to use within the Leaf template file to render them visually. The sector definitions within the type are all about knowledge illustration, however they do not specify the feel and appear of the objects on the admin interface. In different phrases these fields are usually not needed view representations, however extra like knowledge switch objects. We’re going to put the precise view right into a separate Leaf template file in a while. 🍃


The sector varieties are predefined objects within the ViewKit framework, a FormField is an object that encapsulates a generic worth and an elective error message. The FileFormField object is used to switch file knowledge while you need to use a file add discipline inside your type. After you specified the keys that you simply need to use to ship the values, you need to listing these type fields utilizing the fields variable. Every part what’s listed as a discipline shall be robotically validated based mostly on the constraint that you have placed on every discipline (required, size, and so on.).


If you wish to edit a metadata representable mannequin you normally need to ship the metadata data with the mannequin knowledge, you’ll be able to fetch the referenced metadata object by utilizing the findMetadata methodology on a Fluent mannequin, this can load the reference asynchronously. The initialize methodology is a perfect place to carry out async init duties. It’s also possible to override the leafData variable to ship extra data subsequent to the modelId, fields and notification keys.


Because the type is tied to an underlying mannequin, we additionally must learn the mannequin knowledge earlier than we render our type so we will render unique discipline values, and after the consumer submits the shape we would need to write the enter date to the mannequin. In fact the write methodology shall be known as solely when the incoming type fields are legitimate. You may carry out extra database checks you probably have particular validation wants earlier than you really save a mannequin.


The very last item that we need to do is picture processing. We will use the processAfterFields methodology to add our picture into a short lived location, then earlier than the save methodology known as (after the fields are validated), we will use the willSave perform to avoid wasting the picture to a last location and replace our mannequin with the important thing that represents our uploaded picture file. You should utilize this key in a while to render the picture file with the assistance of the Liquid file storage part. 📁


import FeatherCore

last class NewsEditForm: ModelForm 

    typealias Mannequin = NewsModel

    var modelId: UUID?
    var picture = FileFormField(key: "picture").required()
    var title = FormField<String>(key: "title").required().size(max: 250)
    var excerpt = FormField<String>(key: "excerpt").required().size(max: 250)
    var content material = FormField<String>(key: "content material").required()
    var notification: String?

    var metadata: Metadata?

    var fields: [FormFieldRepresentable] 
        [image, title, excerpt, content]
    

    var leafData: LeafData 
        .dictionary([
            "modelId": modelId?.encodeToLeafData() ?? .string(nil),
            "fields": fieldsLeafData,
            "notification": .string(notification),
            "metadata": metadata?.leafData ?? .dictionary(nil),
        ])
    

    init() 

    func initialize(req: Request) -> EventLoopFuture<Void> 
        var future = req.eventLoop.future()
        if let id = modelId 
            future = Mannequin.findMetadata(reference: id, on: req.db).map  [unowned self] in metadata = $0 
        
        return future
    

    func processAfterFields(req: Request) -> EventLoopFuture<Void> 
        picture.uploadTemporaryFile(req: req)
    

    func learn(from enter: Mannequin)  
        title.worth = enter.title
        excerpt.worth = enter.excerpt
        picture.worth.originalKey = enter.imageKey
        content material.worth = enter.content material
    

    func write(to output: Mannequin) 
        output.title = title.worth!
        output.excerpt = excerpt.worth!
        output.content material = content material.worth!
    

    func willSave(req: Request, mannequin: Mannequin) -> EventLoopFuture<Void> 
        picture.save(to: Mannequin.path, req: req).map  key in
            if let key = key 
                mannequin.imageKey = key
            
        
    


The principle purpose why kinds exists is that I wished to separate obligations. A type may help the controller to show a display contained in the CMS utilizing a mannequin, this fashion our controller information shall be smaller and cleaner. Varieties cannot render themselves, so that they nonetheless want a controller object that may management them and a router to register the required URLs that we will use to hook them as much as the admin interface. Varieties normally want one URL that may be reached by a GET request for rendering the preliminary type and one other one which can be utilized to POST (submit) the shape. ↗️


Admin views

The pattern module already consists of the templates that we will must assist information administration. Templates are situated within the Bundle/Templates folder underneath the module listing. The Admin folder comprises subfolders with mannequin names and each single mannequin that we need to handle ought to have a view, edit, delete and listing template file for the CRUD operations. 🔨


These templates comply with the identical sample. Each single on of them begins with a dictionary definiton that’s utilized by one other template (inlined on the finish of the file) to render the view based mostly on the values contained in the dictionary. The admin module offers us a number of templates that we will use to simplify issues. There are pre-baked templates for enter kinds (Admin/Type), lists (Admin/Desk), to current affirmation earlier than delete (Admin/Delete) operation and to easily view the main points (Admin/Element) of a mannequin. You must reap the benefits of these templates if doable. 😉


The admin module additionally provides us reusable type fields. Let’s alter the Edit template, we will add varied enter fields for the shape fields that we outlined beforehand.


#outline(fields):
    #var(discipline = nil)

    #(discipline = ["id": "image", "data": fields.image, "accept": "image/*", "required": true])
    #inline("Admin/Fields/File")

    #(discipline = ["id": "title", "required": true, "data": fields.title])
    #inline("Admin/Fields/Textual content")

    #(discipline = ["id": "excerpt", "size": "s", "data": fields.excerpt])
    #inline("Admin/Fields/Textarea")

    #(discipline = ["id": "content", "size": "xl", "data": fields.content])
    #inline("Admin/Fields/Textarea")

#enddefine


Contained in the Bundle/Templates/Admin/Information/Edit.html file we simply have so as to add three new fields to signify our type fiels as HTML type parts. You should utilize all type of built-in type parts, plus each single type is CSRF and double-submission protected, because of this you’re secure from CSRF assaults by default if you happen to comply with this design sample.



Now if you happen to run the appliance a lot of the performance ought to work with the newly created fields, however earlier than we achieve this, we should always speak about admin controllers.


Admin controllers


The underlying ViperKit framework may help us lots with the required controller setup. Happily Feather comes with an extension that makes issues much more easy if we simply need to present a CRUD interface for a given mannequin. For those who check out the NewsAdminController you may see that you simply solely must setup the referneced module, mannequin and type varieties so as to make issues work. 💪


You may lengthen the performance of controllers by implementing particular lifecycle strategies, for instance we will delete the uploaded picture file from the file storage by utilizing the beforeDelete methodology. Additionally it is doable to increase the listing performance or alter the replace, create strategies, it’s best to check out the AdminViewController protocol in case you are within the particulars.


import FeatherCore
import Fluent

struct NewsAdminController: ViperAdminViewController 

    typealias Module = NewsModule
    typealias Mannequin = NewsModel
    typealias CreateForm = NewsEditForm
    typealias UpdateForm = NewsEditForm

    func listQuery(search: String, queryBuilder: QueryBuilder<Mannequin>, req: Request) 
        queryBuilder.filter(.$title ~~ search)
    

    func beforeDelete(req: Request, mannequin: Mannequin) -> EventLoopFuture<Mannequin> 
        req.fs.delete(key: mannequin.imageKey).map  mannequin 
    


Lengthy story brief, an admin controller has all the pieces that you’re going to must handle your mannequin utilizing the CMS. It will present an inventory, get, create, replace and delete view on your mannequin.


The frontend hooks


If you wish to show information entries on the frontend as devoted pages you need to implement some hook features within the module file. Happily the bottom template already hooks up all the pieces, so we simply have to vary our templates to show extra knowledge.


The “frontend-page” hook can be utilized to fetch metadata objects for a given path and return a response if a mannequin exists for a given slug. It is a nice technique to render information entries in a Website positioning pleasant method. It’s doable to create a separate view object an go the request and the mannequin to it so it will probably render the frontend web page based mostly on a template file.


We’ll add only one little further modification to our frontend view. Would not be cool if we might assist content material filters for the information entries? This fashion we might use the markdown format (if we allow the markdown module) to jot down the content material of a information merchandise. This is tips on how to do it.


struct NewsFrontendView 

    

    func information(_ information: NewsModel) -> EventLoopFuture<View> 
        var ctx = information.leafDataWithJoinedMetadata.dictionary!
        ctx["content"] = .string(information.filter(information.content material, req: req))
        return render("Information", [
            "news": .dictionary(ctx)
        ])
    



It’s also possible to create *-page hooks, that you need to use to render customized templates with the assistance of the Frontend module. The frontend module comes with a web page mannequin, that you need to use to show pages, however it is usually doable to attach a web page mannequin with Swift and Leaf code, that is the way it works in a nutshell. You register a hook perform (in our case “news-page”), then you definately create a brand new web page with the next contents: [news-page]. This syntax implies that you need to name a Feather hook, as an alternative of rendering the contents of the web page, so the system will search for a hook perform with a sound response object and if it finds one, it’s going to use it to render the web page. 🤓


In our case, the “news-page” hook makes use of the Frontend/NewsList template, that’s accountable for displaying the listing of the information entries. For those who click on on an inventory merchandise, the frontend-page hook tries to load the information based mostly on the permalink (referenced metadata slug) and if there’s a match it’s going to render it utilizing the Frontend/Information template.


Feather makes use of the Peacock CSS library to place some model on HTML parts, we will improve our information listing template. Simply alter the Frontend/NewList file, like this:


#outline(physique):

<div class="container">
    <div class="margin margin-bottom-xl">
        <header class="margin-bottom">
            <h1>Information listing</h1>
            <p class="margin-top-zero">Learn the most recent information</p>
        </header>

        <part>
            #for(merchandise in information):
            <div class="background margin-bottom padding shadow">
                <a href="/#(merchandise.metadata.slug)">
                    <img src="#(merchandise.imageKey.resolve())" class="size-width-full">
                    <h2 class="margin-top-zero">#(merchandise.title)</h2>
                    <p class="margin-top-zero">#(merchandise.excerpt)</p>
                </a>
            </div>
            #endfor
        </part>
    </div>
</div>

#enddefine

#inline("Frontend/Index")


The final step is to show a correct information entry with the brand new fields. Within the Frontend/Information file replace the physique and use the next format for the entry web page.


#outline(physique):

<div class="container">
    <div class="margin margin-bottom-xl">
        <h1>#(information.title)</h1>
        <p class="margin-top-zero">#(information.excerpt)</p>
        <img src="#(information.imageKey.resolve())" class="size-width-full margin-vertical">
        #(information.content material)
    </div>
</div>

#enddefine

#inline("Frontend/Index")


Now if you happen to construct and run all the pieces, first you may must run the installer, after that you could log in to the admin interface and you may create & publish your very first information entry. You may learn extra about tips on how to use Feather CMS, simply learn the consumer guide. As a free of charge it’s best to be capable to apply content material filters to your information merchandise, so you’ll be able to reap the benefits of the built-in markdown or the Swift syntax highlighter filters. 🤩



Abstract

On this tutorial we have created a model new module for Feather CMS utilizing loads of underlying frameworks and instruments. This may be arduous at first sight, however I actually love this method as a result of I can give attention to defining my enterprise fashions as an alternative of taking good care of smaller particulars reminiscent of registering the required routes for modifying a database entry. Feather CMS will disguise this type of complexity and supply dynamic extension factors for constructing your admin interfaces. On the frontend facet you’ll be able to simply lengthen the dynamic routing system, apply content material filters and even add your individual extension factors by hook features.

There’s a lot extra to speak about, however this time I will cease proper right here, if you happen to loved this tutorial please comply with me on twitter, subscribe to my publication or contemplate supporting me by buying my Practical Server Side Swift e book on Gumroad.




Source link

You might also like

Encoding and decoding data using the Hummingbird framework

Hummingbird routing and requests – The.Swift.Dev.

Building a Scrollable Custom Tab Bar in SwiftUI

Share30Tweet19
learningcode_x1mckf

learningcode_x1mckf

Recommended For You

Encoding and decoding data using the Hummingbird framework

by learningcode_x1mckf
March 22, 2023
0
Encoding and decoding data using the Hummingbird framework

HTTP is all about sending and receiving information over the community. Initially it was solely utilized to switch HTML paperwork, however these days we use HTTP to switch...

Read more

Hummingbird routing and requests – The.Swift.Dev.

by learningcode_x1mckf
March 17, 2023
0
Hummingbird routing and requests – The.Swift.Dev.

Routing on the server facet means the server goes to ship a response primarily based on the URL path that the consumer referred to as when firing up...

Read more

Building a Scrollable Custom Tab Bar in SwiftUI

by learningcode_x1mckf
March 10, 2023
0
Building a Scrollable Custom Tab Bar in SwiftUI

Whether or not you’re making a social media app or a productiveness device, the tab bar interface can improve the consumer expertise by making it extra intuitive and...

Read more

Beginner’s guide to server-side Swift using the Hummingbird framework

by learningcode_x1mckf
March 8, 2023
0
Beginner’s guide to server-side Swift using the Hummingbird framework

Swift on the Server in 2023 Three years in the past I began to focus on Vapor, the preferred web-framework written in Swift, which served me very...

Read more

What’s new in Swift 5.8 – Hacking with Swift

by learningcode_x1mckf
March 8, 2023
0
What’s new in Swift 5.8 – Hacking with Swift

Though many main Swift modifications are at present percolating by way of Swift Evolution, Swift 5.8 itself is extra of a clear up launch: there are additions, sure,...

Read more
Next Post
Node.js creator Ryan Dahl urges Oracle to release JavaScript trademark • DEVCLASS

'The best thing we can do today to JavaScript is to retire it,' says JSON creator Douglas Crockford • DEVCLASS

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Related News

Google expands open source bounties, will soon support Javascript fuzzing too – ZDNet

JavaScript, Java, and Python skills top demand – InfoWorld

February 12, 2023

Conan C/C++ Package Manager 2.0 Now In Beta

January 17, 2023
Google expands open source bounties, will soon support Javascript fuzzing too – ZDNet

20 million JavaScript devs can now build applications on NEAR: KBW 2022 – Cointelegraph

March 8, 2023

Browse by Category

  • C#
  • C++
  • Java
  • JavaScript
  • Python
  • Swift

RECENT POSTS

  • Java Developer Survey Reveals Increased Need for Java … – PR Newswire
  • What You Should Definitely Pay Attention to When Hiring Java Developers – Modern Diplomacy
  • Java Web Frameworks Software Market Research Report 2023 … – Los Alamos Monitor

CATEGORIES

  • C#
  • C++
  • Java
  • JavaScript
  • Python
  • Swift

© 2022 Copyright Learning Code

No Result
View All Result
  • Home
  • JavaScript
  • Java
  • Python
  • Swift
  • C++
  • C#

© 2022 Copyright Learning Code

Are you sure want to unlock this post?
Unlock left : 0
Are you sure want to cancel subscription?