Is Laravel Nova a threat for WordPress?

Glenn Mulleners
Spartner
Published in
12 min readApr 5, 2019

Laravel Nova and WordPress can both be used as a Content Management System (CMS). Nova was introduced last year (2018). WordPress has been around much longer (2003).

What do both packages have in common and at which points are they different? That’s what I’m going to have a look at in this article.

Use and target audience

Nova was built with the developer in mind. It is truly meant as a solution to provide beautifully designed administration panels in a quick and relatively easy way.

Yep, you read that right. Its core purpose is to manage content, which makes content editors (and developers of course) its target audience.

WordPress has been developed with another scope. It is a powerful semantic publishing platform with a fast, light and free content management system with sensible default settings and features, and an extremely customizable core.

So, WordPress can be used to both manage the content in the administration panel ánd to display it on the front-end. Its target audience is thus much bigger (content editors, developers and site visitors).

Installation

Before we can dig deeper into the differences between WordPress and Nova, we need to get both packages installed.

Installing Nova

Installing Nova takes a bit more effort than installing WordPress since it’s an additional package on top of Laravel.

  • Install Laravel
    - Setup database
    - Setup Laravel project
  • Install Nova
    - Edit composer.json
    - Run composer update
    - Run php artisan nova:install
    - Run php artisan migrate
    - Setup a Nova user: php artisan nova:user

Installing WordPress

The installation of WordPress is quite easy.

  • Just refer to the famous 5-minute installation
    - Download the package
    - Setup database
  • Via the installer:
    - configure WordPress (site and database information)
    - Setup a user for the website

Some basic features compared

Now that we have both packages installed, let’s see what we get out of the box for each package.

Nova

The core of Nova consists of what are called “Resources”.

A Nova resource corresponds to an Eloquent model in your application.

Only a User resource will be present in our default Nova installation. Combined with the default installation of Laravel, we ended up with a basic authentication system (including password resets) and a user management system.

WordPress

Originally, WordPress started as a system for maintaining a blog. We can still recognise this in the default installation. It not only ships with Users, but also with Posts, Media, Pages and Comments (and it’s still gaining in popularity by the way):

Dashboard

The first feature both applications have in common is the Dashboard.

Nova

On the Nova dashboard, we only find references to the documentation to get you (as a developer) familiar with Nova. This is because it is designed to exist on top of a Laravel installation where you develop each CMS component yourself. Of course it’s possible to modify this dashboard with e.g. coding some metrics.

WordPress

The dashboard in WordPress shows some statistics and provides some quick entry points for common actions (like creating posts, managing comments etc.)

The items that should be visible on the dashboard can be customised via the screen options (you can find this button on top of the screen).

Users

There’s one other element that both Nova and WordPress have in common: Users. However, they aren’t exactly the same. There are some major differences in functionalities as well as the user interface, which you’ll discover in a minute.

Nova

You can find the specifications for the User resource in App/Nova/User.php.

We can view/edit the most important pieces of information for a user via the default route: nova/resources/users:

  • Name
  • Email address
  • Password
  • ID (auto-generated)
  • Avatar (auto-generated via Gravatar)

In the database, there’s some more information (such as when the user was created, when the information was updated last and some information used by system functionality, such as email verification and remember me). This information is not visible in the Nova application by default.

WordPress

The information for a user in WordPress is very extensive. The view/edit user screen (available via wp-admin/users.php) includes:

  • Username (cannot be changed)
  • First name
  • Last name
  • Nickname (required)
  • Display name
  • Email address
  • Website
  • Biographical info (paragraph)
  • Profile picture (auto-generated via Gravatar)
  • Information about personal options
    - Visual editor (enable/disable)
    - Syntax highlighting (enable/disable)
    - Admin color scheme (choices)
    - Keyboard shortcuts (enable/disable)
    - Show/hide toolbar when viewing the site (show/hide)

It is possible to e.g. add extra fields to the user. You’d have to custom code it yourself or search for a plugin to get the job done. This might take a bit more effort than it would take with Nova.

Creating new users

When creating a new user in the application, some significant differences can be seen too.

Nova

You only need to provide a name, an email address and a password. The password length must be at least 6 characters. Nova doesn’t provide the option to email the newly created user their credentials, but with some Laravel knowledge it’s easy to add.

To enhance Nova with a password generation feature and extended controls for the password (like excludeRules), you’d have to include another package, e.g. https://github.com/naifalshaye/nova-random-password-generation.

WordPress

For WordPress, the same fields (username, email and password) are required. There are extra fields to provide additional information like first and last name.

WordPress includes random password generation and has a password strength indicator, which asks you to confirm the use of a weak password (if applicable). There’s also the option to send the new user an email with their login information. Last, WordPress also supports user roles.

Posts

Next part of our comparison is Posts.

Nova

Since Nova only ships with the User resource, we need to create a Post resource.

First, let’s create our Post model and some migrations:

php artisan make:model Post -m
php artisan make:seeder PostsTableSeeder

Finally, we should end up with the following files:

Post model

<?php

namespace
App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class Post extends Model
{
/**
*
@return BelongsTo
*/
public function author()
{
return $this->belongsTo(User::class, 'post_author', 'id');
}
}

User model

<?php

namespace
App;

use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

class User extends Authenticatable
{
use Notifiable;

/**
* The attributes that are mass assignable.
*
*
@var array
*/
protected $fillable = [
'name', 'email', 'password',
];

/**
* The attributes that should be hidden for arrays.
*
*
@var array
*/
protected $hidden = [
'password', 'remember_token',
];

/**
* The attributes that should be cast to native types.
*
*
@var array
*/
protected $casts = [
'email_verified_at' => 'datetime',
];

/**
*
@return HasMany
*/
public function posts()
{
return $this->hasMany(Post::class, 'post_author', 'id');
}
}

Migration

This migration creates a posts table similar to the wp_posts table in WordPress. For simplicity, some functionalities have been omitted.

<?php

use
Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreatePostsTable extends Migration
{
/**
* Run the migrations.
*
*
@return void
*/
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('post_author')->default(0);
$table->longText('post_content');
$table->text('post_title');
$table->text('post_excerpt');
$table->string('post_status', 20)->default('publish');
$table->timestamps();
});
}

/**
* Reverse the migrations.
*
*
@return void
*/
public function down()
{
Schema::dropIfExists('posts');
}
}

Posts table seeder

<?php

use
Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;

class PostsTableSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run()
{
DB::table('posts')->insert([
'post_author' => 1,
'post_content' => '<p>Welcome to WordPress. This is your first post. Edit or delete it, then start writing!</p>',
'post_title' => 'Hello world!',
'post_excerpt' => '',
'created_at' => now(),
'updated_at' => now(),
]);
}
}

Database seeder

<?php

use
Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
*/
public function run()
{
$this->call(PostsTableSeeder::class);
}
}

Next, we need to generate the Nova resource for our Post model:

php artisan nova:resource Post

After some adjustments, we’ll end up with:

User resource

<?php

namespace
App\Nova;

use Illuminate\Http\Request;
use Laravel\Nova\Fields\Gravatar;
use Laravel\Nova\Fields\HasMany;
use Laravel\Nova\Fields\ID;
use Laravel\Nova\Fields\Password;
use Laravel\Nova\Fields\Text;

class User extends Resource
{
/**
* The model the resource corresponds to.
*
*
@var string
*/
public static $model = 'App\\User';

/**
* The single value that should be used to represent the resource when being displayed.
*
*
@var string
*/
public static $title = 'name';

/**
* The columns that should be searched.
*
*
@var array
*/
public static $search = [
'id', 'name', 'email',
];

/**
* Get the fields displayed by the resource.
*
*
@param \Illuminate\Http\Request $request
*
*
@return array
*/
public function fields(Request $request)
{
return [
ID::make()->sortable(),

Gravatar::make(),

Text::make('Name')
->sortable()
->rules('required', 'max:255'),

Text::make('Email')
->sortable()
->rules('required', 'email', 'max:254')
->creationRules('unique:users,email')
->updateRules('unique:users,email,{{resourceId}}'),

Password::make('Password')
->onlyOnForms()
->creationRules('required', 'string', 'min:6')
->updateRules('nullable', 'string', 'min:6'),

HasMany::make('Posts'),
];
}

/**
* Get the cards available for the request.
*
*
@param \Illuminate\Http\Request $request
*
*
@return array
*/
public function cards(Request $request)
{
return [];
}

/**
* Get the filters available for the resource.
*
*
@param \Illuminate\Http\Request $request
*
*
@return array
*/
public function filters(Request $request)
{
return [];
}

/**
* Get the lenses available for the resource.
*
*
@param \Illuminate\Http\Request $request
*
*
@return array
*/
public function lenses(Request $request)
{
return [];
}

/**
* Get the actions available for the resource.
*
*
@param \Illuminate\Http\Request $request
*
*
@return array
*/
public function actions(Request $request)
{
return [];
}
}

Post resource

<?php

namespace
App\Nova;

use Illuminate\Http\Request;
use Laravel\Nova\Fields\BelongsTo;
use Laravel\Nova\Fields\DateTime;
use Laravel\Nova\Fields\ID;
use Laravel\Nova\Fields\Select;
use Laravel\Nova\Fields\Text;
use Laravel\Nova\Fields\Textarea;
use Laravel\Nova\Fields\Trix;

class Post extends Resource
{
/**
* The model the resource corresponds to.
*
*
@var string
*/
public static $model = 'App\Post';

/**
* The single value that should be used to represent the resource when being displayed.
*
*
@var string
*/
public static $title = 'post_title';

/**
* The columns that should be searched.
*
*
@var array
*/
public static $search = [
'id',
'post_title',
'post_content',
];

/**
* Get the fields displayed by the resource.
*
*
@param \Illuminate\Http\Request $request
*
*
@return array
*/
public function fields(Request $request)
{
return [
ID::make()->hideFromIndex(),
Text::make('Title', 'post_title')->sortable(),
Trix::make('Body', 'post_content'),
Textarea::make('Excerpt', 'post_excerpt'),
Select::make('Status', 'post_status')->options([
'publish' => 'Public',
'private' => 'Private',
])->displayUsingLabels()->rules('required'),
DateTime::make('Date', 'created_at')->format('MMM DD, YYYY h:mm a '),
BelongsTo::make('Author', 'author', User::class),
];
}

/**
* Get the cards available for the request.
*
*
@param \Illuminate\Http\Request $request
*
*
@return array
*/
public function cards(Request $request)
{
return [];
}

/**
* Get the filters available for the resource.
*
*
@param \Illuminate\Http\Request $request
*
*
@return array
*/
public function filters(Request $request)
{
return [];
}

/**
* Get the lenses available for the resource.
*
*
@param \Illuminate\Http\Request $request
*
*
@return array
*/
public function lenses(Request $request)
{
return [];
}

/**
* Get the actions available for the resource.
*
*
@param \Illuminate\Http\Request $request
*
*
@return array
*/
public function actions(Request $request)
{
return [];
}
}
This is how our Post resource looks like in Nova (after seeding the database).

WordPress

WordPress comes with Posts by default, so we don’t have to code anything.

WordPress even has some extra items, which I did not cover in my Nova application:

  • Permalink
  • Categories
  • Tags
  • Featured Image
  • Comments
  • Password-protection
  • Sticky post

Nova vs. WordPress: the most eye-catching differences

Now we have similar functionalities in both applications, let’s have a look at where we can spot differences.

Overview

The basic Posts overview of Nova looks similar to that in WordPress. When we’d like to view a list of Posts for a specific user, there’s a significant difference from a UI-perspective.

Nova
By clicking on the author name, we go to the author page and see it’s related models, e.g. the Posts created by this user. However, we only have one model relationship at this point. Imagine having multiple related models displayed, all on one page. With only a few relationships, it’s still doable.

Not to mention that I first see the user details on the page, when I’m actually interested in viewing the Posts.

WordPress
Clicking on the name of the Post Author in WordPress just gives us a list of Posts for that user. Same interface as we’re used to when viewing Posts, nothing more, nothing less.

Viewing

Nova has an option to view the details of the Post. In some cases (e.g. the Body and Excerpt), we need to click Show Content before we can actually view the contents for those fields.

WordPress has a Quick Edit mode to show the most important information at a glance.

Editing

Nova
The WYSIWYG-editor from Nova is Trix. I’ve reviewed this editor in another blog, so I won’t go into details here.

WordPress
WordPress uses the new Gutenberg editor, which is a block-based editor. This makes editing content really easy. If you don’t like it, you can still use the Classic Editor plugin however.

Deleting

Nova
In Nova, a delete action is irreversible. Once the resource (e.g. the post) has been deleted, it’s gone (unless you use SoftDeletes).

WordPress
In WordPress we can Trash a post. The post will be moved to Trash so it can be restored later if needed.

Database

Nova
In Nova, there’s a separate table for each model. This makes the database structure very clean. The posts table only contains posts.

WordPress
In terms of database structure, WordPress seems a bit more cluttered. All entries for a (custom) post type are stored in the wp_posts table.

This means the table grows very fast. With only the basic WordPress application set up, we already have some entries with different post_type values (post, page, revision).

Other points to take into consideration

Revisions

Revisions are older versions of the content (so you could revert to a specific version if you’d like), which is something that Nova does not support out of the box, but it can be added manually of course.

Auto-saving

This is supported by WordPress. Nova lacks this feature (unless you develop this yourself).

Front-end

Nova is a beautifully designed administration panel for Laravel.

This quote (from Laravel Nova website) states it all. Nova is purely meant for administrative purposes. With WordPress, it’s possible to build a complete website since it comes with a theme (for the website) already installed.

Conclusion

In the end it comes down to comparing apples and oranges. There’s not really good or bad. It all depends on what you need.

A simple CRUD-system for your database? Nova works great and provides a modern-looking UI. You can customise and extend it (e.g. with filters, lenses and actions), though it takes some time and development skills in order to achieve some nice looking results. You can even extend Nova with packages (like our Laravel Nova Excel package).

If you’d like the additional features of WordPress that Nova doesn’t support out of the box, then go with WordPress. It’s a good solution if you need to whip up a Content Management System with basic functionalities like user management, page editing, menu building etc. You can extend WordPress with plugins.

Keep in mind however that finding the right plugins and themes could take some time. With Nova, you’d still need to build the front-end part of your site as well.

So, if you’re an end-user; you should outsource the development or stick to WordPress. If you’re a developer, you can choose Nova or WordPress.

Finally, Nova is a paid package, while WordPress is available as a (free) open-source package.

Further reading

When you’d like to dig deeper into Nova, I can recommend looking into the official documentation or watch the (free) Laravel Nova Mastery video course.

If you’re convinced that WordPress is the right choice for you, you can have a look at the WordPress Support page to get started.

Written by Glenn Mulleners, Developer at Maatwebsite

--

--