User management in K3

Hi there

I’m stumped on a new project and would love to get some opinions/help on it. The site is content heavy (both text and images), which I feel like Kirby is perfect for, but also has to deal with a fairly complex user management and event system. Now I feel like I have two options here:

  • Build everything in Kirby:
    This would be my preferred choice but I don’t know how feasible it is. If I have to manage up to a couple thousand users and have relationships between different types of users and certain events they attend, I’d rather have a RDMBS (could be SQLite though) for that. I’m wondering how adaptable the K3 user system is to working with a database. Could I replicate the User class but extend it to save to a database instead of file, while keeping the functionality of the current User implementation and keep it compatible with the rest of Kirby? If I go down this route should I implement the entire event management system as a plugin? Maybe even a separate user plugin and events plugin?

  • Build the content in Kirby and keep the user/event management in a seperate Symfony app:
    I don’t really want to have the client maintain 2 systems if I can help it but I could easily build this kind of thing in Symfony and just have certain Kirby pages link to form pages in my Symfony app. Maybe even an API integration or some sort of common auth so Kirby sessions are recognised in my own app? I don’t know how flexible this option would be and I’m sure the whole auth situation could be a lot more work than I expect.

These users, do they need to be Kirby users (with or without Panel access)?

They would not need Panel access. I’d handle their signup/login/password reset/etc through front-end forms and write to this SQLite database if I go with this option. Maybe users is not the correct term for them, they’re more like members or customers. Members would not need backend access.

Are you proposing I create these members as a completely separate object from the Kirby User’s? I was hoping to hijack the User login flow, with how sessions are used, but I guess I could implement my own small membership class? Something like

$session->data()->set('', $this->id());

Would not play well with a custom defined Member class?

@dreadnip Curious how you chose to implement this? I have a similar project.

I went with a combination of standard Kirby users (with a front-end register and login), events as pages (with a custom blueprint/template) and an SQLite database which links the two together. On the event pages I show a form to logged in users. When submitted I enter a record in my SQLIte DB with the event ID and logged in user’s ID. Then in the back-end I have an export function which prints a CSV of all the events and their attendees.


I’m currently in the same situation:

Need to manage a userbase of 1000+ clients in the panel, but these users are not kirby users - meaning they don’t need to login to the panel. But they need to be able to login into “frontend” just to keep their information updated. I want to store these information into sqlite/mysql.

Could you please explain a little bit more about your setup? Or maybe give a little example of how the workflow looks and what you did with controllers, models or even with the frontend register and login?

Thank you very much

I used a combination of this cookbook recipe and this project to create my user flow. There is a login page, register page, forgot password, etc… . Users are stored as Kirby users with a “member” role which has no panel access. All of this is done in controllers.

For the events I have a Course blueprint where a UUID can be generated when a new event is added. I also have a Course page model which has the DB interaction snippets


class CoursePage extends Page {

     * Check whether or not a user is already subscribed to this event
     * so we can hide the Subscribe snippet.
  	public function isSubscribed(string $userId): bool
	    $record = Db::table('user_event')
	    		'user_id' => $userId,
	    		'event_id' => $this->id()

		if (!$record) {
			return false;

		return true;

  	public function subscribe(string $userId): void
  		//write to db with given user ID & page event ID
    	Db::insert('user_event', [
		  'user_id' => $userId,
		  'event_id' => $this->id()

then in my Course controller I have this:


return function ($kirby, $page) {

    // Not logged in
    if ($kirby->user() === null) {
        return [
            'message' => 'Inschrijven kan enkel wanneer u aangemeld bent met een Bleau account.'
    // Get user data
    $userId = $kirby->user()->id();
    $isSubscribed = $page->isSubscribed($userId);

    // Already subscribed
    if ($isSubscribed) {
        return [
            'message' => 'U bent al ingeschreven voor deze cursus.'

    // When the subscribe button is clicked
    if ($kirby->request()->is('post')) {
        // And the user isn't subscribed to this event
    	if (!$isSubscribed) {
    	    // Subscribe him!

            return [
                'message' => "U werd ingeschreven voor deze cursus."

then in my Course template I have this at the bottom

<div class="subscribe">
    {% if message is defined %}
        {{ message }}
    {% else %}
        {{ snippet('subscribe') }}
    {% endif %}

Pretty basic, but it does what it has to do :slight_smile: