Other reviews in this series:
- FlarePoint: Laravel-based free CRM
- Invoice Ninja – Laravel-powered solution for better invoicing
- Attendize – event tickets selling system based on Laravel 5
- Ribbbon – project management system on Laravel 5.1 and Vue.js
- Faveo: impressive helpdesk system built on Laravel
- Confomo: Laravel-based website to meet Twitter friends at conferences
First things first - Invoice Ninja has a really interesting business model. They have two different versions - hosted and self-hosted. In other words, you can register at invoiceninja.com and use their online version, paying for premium services, or you can download the code for free (Laravel 5.2) and install it on your server for free. It reminds me of another tool that has hosted and self-hosted open-source version - it's analytics app called Piwik. Anyway, we will be talking about self-hosted version here - we're more interested in Laravel code here. So let's begin.
Installation
Interestingly, though the code is available on Github, in their official documentation in Readme they recommend not to clone repository code, but to download ZIP file. And it's actually quite heavy - 91 MB as ZIP and 247 MB unpacked.



Usage
Ok, let's go on and log in. After logging in, I got into a dashboard. Empty, of course, but quite impressive.




Laravel code
Ok, and finally - what's under the hood? How good/complex is Laravel code, is it easy to customize? I usually start judging the quality from routes file - in this case it's Laravel 5.2 so it's app/Http/routes.php. And it's actually HUGE:// Application setup Route::get('/setup', 'AppController@showSetup'); Route::post('/setup', 'AppController@doSetup'); Route::get('/install', 'AppController@install'); Route::get('/update', 'AppController@update'); // Public pages Route::get('/', 'HomeController@showIndex'); Route::get('/terms', 'HomeController@showTerms'); Route::get('/log_error', 'HomeController@logError'); Route::get('/invoice_now', 'HomeController@invoiceNow'); Route::get('/keep_alive', 'HomeController@keepAlive'); Route::post('/get_started', 'AccountController@getStarted'); // Client visible pages Route::group(['middleware' => 'auth:client'], function() { Route::get('view/{invitation_key}', 'ClientPortalController@view'); Route::get('download/{invitation_key}', 'ClientPortalController@download'); Route::get('view', 'HomeController@viewLogo'); Route::get('approve/{invitation_key}', 'QuoteController@approve'); // ...Total 350 lines of code in routes. And I believe it's not really too much - the application is that big, seriously. What surprised me there is that in routes file there are constants defined at the end:
if (!defined('CONTACT_EMAIL')) { define('CONTACT_EMAIL', Config::get('mail.from.address')); define('CONTACT_NAME', Config::get('mail.from.name')); define('SITE_URL', Config::get('app.url')); define('ENV_DEVELOPMENT', 'local'); define('ENV_STAGING', 'staging'); define('RECENTLY_VIEWED', 'recent_history'); // ...In fact, another 500 lines of code for constants. I probably wouldn't put it in routes file, but as long as it works... there might have been some logic in that. Also at the very end routes contains a few functions. Yes, functions, just like that. Go figure.
function uctrans($text) { return ucwords(trans($text)); } // optional trans: only return the string if it's translated function otrans($text) { $locale = Session::get(SESSION_LOCALE); if ($locale == 'en') { return trans($text); } else { $string = trans($text); $english = trans($text, [], 'en'); return $string != $english ? $string : ''; } }Now, let's take a look at one of the controllers - for example, ClientController.php:
protected $clientService; protected $clientRepo; protected $entityType = ENTITY_CLIENT; public function __construct(ClientRepository $clientRepo, ClientService $clientService) { //parent::__construct(); $this->clientRepo = $clientRepo; $this->clientService = $clientService; } /** * Display a listing of the resource. * * @return Response */ public function index() { return View::make('list', [ 'entityType' => ENTITY_CLIENT, 'title' => trans('texts.clients'), 'sortCol' => '4', 'columns' => Utils::trans([ 'checkbox', 'client', 'contact', 'email', 'date_created', 'last_login', 'balance', '' ]), ]); }Services and Repositories. Good. So should be a good flexible architecture underneath. View::make() syntax looks sooo Laravel 4, but it's pretty understandable because Invoice Ninja is a couple years old, so I guess the upgrades to newer Laravel versions ended up pretty "hacky". Also there's one list.blade.php for everything - might sound convenient, depends on a developer style, probably. All in all, the code seems a little questionable - although it works well and should be easy to maintain and customize. Also, I can say kind words about their creators involvement in the community and being open - they have open Trello board with roadmap, and also discuss their plans on Github. So, in conclusion, if you want to trust your invoices for a tool based on Laravel, Invoice Ninja is a perfect and solid choice.
Other reviews in this series:
- FlarePoint: Laravel-based free CRM
- Invoice Ninja – Laravel-powered solution for better invoicing
- Attendize – event tickets selling system based on Laravel 5
- Ribbbon – project management system on Laravel 5.1 and Vue.js
- Faveo: impressive helpdesk system built on Laravel
- Confomo: Laravel-based website to meet Twitter friends at conferences
No comments or questions yet...