Interesting use cases

I used Kirby to build a SAAS product: languageinmotion.jp.

There are some SQLite databases used but that’s it. That you can build a complete service almost entirely with flat files was a happy discovery and a very pleasant experience. Thank you Bastian for making life simple. Before anyone else dishes out their time in learning Laravel or Rails or similar, Kirby works quite well for this.

If anyone is interested in the back-end workings, let me know and I can post details.

3 Likes

That’s great! And yes, so much can be done with Kirby… You should definitely share some insights into your backend. Are you using the panel?

Thanks Thiousi!

Here is some of how it’s set up (with some changes to make it clearer):

Model (as in MVC)

In the content folder I created a page named “app”. Within that page I created sub-pages for each model. So my folder structure looks like:

content/app/user
content/app/book

All of those pages are empty and simply are to allow me to create Kirby models for them:

site/models/user.php
site/models/book.php

Kirby models can be treated like any PHP class so within them I have typical CRUD functions, like:

// site/models/user.php

class UserPage extends Page {
    
  public function create() {
    $error = false;
    $messages = array();
    
    try {
      $user = site()->users()->create(array(
        'username'  => get('username'),
        'email'     => get('email'),
        'password'  => get('password'),
        'language'  => 'en',
        'role'      => ‘customer’
    } catch(Exception $e) {
      $error = true;
      $messages[] = 'The user could not be created. ' . $e->getMessage();
    }
    
    return compact('error', 'messages'); // Explained below in "Linking them all together"
  }

}

Notes:

I have a model called “user” but unlike in traditional MVC where the model properties (e.g., username, email, password) are defined in the model, my “user” model is just an abstraction of Kirby’s user object and as such I let Kirby handle those property definitions.

Not having to create a user management system from scratch (especially considering security) was one of the reasons I went with Kirby instead of Laravel or Rails.

As a result it’s very simple:

  1. A user submits a POST request to /signup: tell Kirby to create user account.
  2. A user submits a POST request to /login: tell Kirby to validate username and password and login.
  3. A user submits a GET request to /logout: check if user is logged in, then tell Kirby to log the user out.

(All these functions are explained in the Cheatsheet.)

View (as in MVC)

Views are no different than normal pages. So for creating a user, I have this page: content/signup and its accompanying template in: site/templates/signup.php

Controller (as in MVC)

My MVC paradigm gets a little confused here, but my controllers’ responsibilities are mostly redirecting users away from pages they shouldn’t have access to.

For example, if a non-logged in user tries to access the /profile page, he or she will be redirected to the /login page.

Linking them all together

I created a plugin titled “API”. The whole plugin consists of a list of routes to perform CRUD operations on the models. For example:

// site/plugins/api/api.php

kirby()->routes(
  array(
    array(
      'pattern' => 'signup', 
      'method'  => 'POST',
      'action' => function() {
    
        $user = page('app/user');
        $data = $user->create();
    
        if ($data['error']) {
          return array('signup', array('error' => $data['error'], 'messages' => $data['messages']);
        } else {
          go('profile');
        }
      }
    )
);

Notes:

  • Route is only called by a POST request (i.e. the user submits a form on the signup page).
  • $data contains the response of the user model. If an error occurs, then the signup page is shown again and the error message passed in as a template variable. Errors are shown in the signup template like:
<?php if (isset($error)): ?>
  <div class="errors">
    <?php foreach ($messages as $message): ?>
      <?php echo $message ?><br> 
    <?php endforeach ?>
  </div>
<?php endif ?>

In conclusion

That’s a simplified explanation but hopefully it somewhat shows how easy it is. So to answer your question, the panel is only used by and accessible to me, while all users use the frontend. Let me know if you’d like to know anything else.

9 Likes

Thanks for sharing :slight_smile:. Always great to see what people do with Kirby.

Thanks a lot for taking the time to write about this!
One question comes to mind, and take it with a pinch of salt from somewhat not very familiar with MVCs. I’ve never used Models used in Kirby before, what’s the advantage of using models over plugins or controllers?

For the difference between controllers and models, see this post:

2 Likes

Thanks for sharing that! Did I miss that in the docs? I tried to find it but couldn’t

Well, no, nothing directly comparing controllers & models, but I have it on my list. It’s a pretty long list …

Do you want us to try and help with the list ?

Hi

I’ve been working on an invoicing system for myself over past couple of weeks. My goal was to get a simple system running locally. From here I can add clients and different billing accounts. Also, I can bill in different currencies per invoice, I set an exchange rate and the panel will convert the foreign currency back to my own so that I can keep track of incoming invoices in my own currency (excluding VAT).

Dashboard

For the dashboard I took inspiration from many of our existing plugins/widgets for drafts and stats. These were used as a basis for creating an overview of existing invoice statuses and affairs, that is to keep track of drafts, pending, paid and overdue payments. Invoices are stored in content folders organised by year which I use to generate data for a graph that shows income five years back.

Content Structure & Controller

  • Billing accounts are added as children to my billing preferences including VAT-rate and my address details.
  • Clients and their details are stored under a clients folder.
  • Invoices are added as children under a folder for each given tax year.

The content folder structure works like this:

content/billing
content/billing/bank.account
content/clients
content/invoices/2016

Much of the data is stored as structure fields and all pages reference each other with a unique id so that I can’t accidentally break a link between an invoice, its client and billing details. An invoice template takes care of accessing all the different data that the invoice needs. Here is my invoice.php controller that I use to access pages of data references by unique id’s:

# site/controller/invoice.php

return function($site, $pages, $page) {

	// Company Details
	$company = page('billing/');
	$companyAddressFormat = strtolower($company->addressformat());

	// Client
	$client = page(page('clients')->children()->findBy('autoid', $page->client()->value()));
	if($client) {
		$clientAddressFormat = strtolower($client->addressformat());
	}

	$currency = page('billing')->currency();
	$vat = page('billing')->vat()->float();

	$billing = page(page('billing')->children()->findBy('autoid', $page->billing()->value()));

	return compact('company',
		           'companyAddressFormat',

		           'client',
		           'clientAddressFormat',

		           'currency',
		           'vat',

		           'billing'
	);

};

Using a model for the invoice.php template I perform the calculations for invoice subtotals and adding VAT based on the default VAT-rate.

Creating an Invoice

Creating an invoice is simple. You give it a title, invoice number and select a client from a dropdown menu and also which billing account details you wish to give the client along with an optional note that is displayed on the actual invoice (details regarding overdue payments etc.)

Currently I’ve built two structure fields for adding items to an invoice—either a quantity/rate (also for time based billing) and a fixed rate. The currency applied to the specific invoice is listed beside each inventory item.

At the moment I define my base currency under a billings page where I manage my own details (address, billing accounts, default VAT-rate etc.) I can assign a custom currency per invoice and its exchange rate that will calculate the amount earned in my base currency so that I have an overview of how much tax I’ll have to pay by the end of the year—this I do manually at the moment.

Now for the fun part. The system takes all the information I’ve linked to an invoice and generates it as a page, so I can just hit the preview button and print the page as a PDF and send it off to my clients. When it’s paid and can mark it off under the panel so I can keep track of incoming payments etc.

I’ve set up some basic CSS print styles that makes sure everything looks nice and simple when I print the PDF. Certainly having fun putting the whole thing together!

Oliver

9 Likes

Looks awesome! I wanted to build such an invoicing system with Kirby for more than a year but never had the time.

Thanks :wink: Thanks again, @lukasbestle, for helping me out get in touch with Jimmy last week!

I am by no means a pro programmer, so there are definite improvements that could be made for further extendibility in terms of creating a model to generate estimates etc. Not quite sure what the next step is at the moment, but it functions quite well in its current form. Any ideas?

Well, such a system always needs to work for the one who uses it.

I would probably add all accounting stuff into it (including versioning with Git to have it conform to German law), but that’s definitely a very huge project (and maybe the reason why I never got to build it ;)).

Yes, I think it also applies here, I’m working in between Denmark and Norway. Manual backups are the way at the moment :smiley:

Perhaps I should write up a more detailed readme on everything I’ve included and how I’ve structured everything. Don’t know if anyone is interested in building git versioning for it or improving the build…?

The benefits of having your own invoicing system in Kirby is clear to me, you can build whatever module you need, be it for different statistics or adding a payment portal with stripe or paypal…

Manual backups wouldn’t be OK in Germany as you need to make sure that each accounting entry can’t be changed without traces. It’s all a bit difficult and in the end the administration probably won’t accept Git anyway because they have never heard of it. :wink:

I see, yes that does make it a lot trickier :dizzy_face:
Do any other options than git come to mind? Building a versioning system from scratch is a bit of an adventure. Do you think it would be worth taking the project that step further?

I would still go for Git, it’s well-known, pretty robust and at least better and more trustable than a homemade solution.

If that kind of tamper protection is required in Denmark/Norway, I’d definitely do it. Otherwise your whole accounting is going to be disregarded. :slight_smile:
But as long as you only track your clients and invoices, it should be fine (the invoices need to be archived of course). Just don’t start tracking your accounting entries. :wink:

In Denmark there is not such a requirement, not entirely sure about Norway. I operate from Denmark at the moment… Hurrah for now :wink:

Will have to look into this in greater detail. Unfortunately I’m not confident/skilled enough to implement git by myself.

Perhaps there should a whole new thread/git repo for further development sometime. Depends on interest I suppose.

Wondering about this auto-git plugin, does a log of changes to files suffice or does it need to be baked into the invoice itself in Germany?

Yes, Auto Git was what I thought of as well.
I don’t know if Git suffices at all, but if it does, a log should be enough. But: INAL. :wink: