A simple introduction to a Kanban project written in PHP——jitamin

A simple introduction to a Kanban project written in PHP——jitamin

JITAMIN is a project management system written in PHP With Kanban as its main project tool. It has laravel-like directory structure, but in fact uses different packages like Pimple for container, symfony/event-dispatcher for event dispatching, robmorgan/phinx for migration.

Have a look at the project’s package.json file.

"require" : {
    ......
    "pimple/pimple" : "3.0.2",
    "eluceo/ical": "0.10.1",
    "erusev/parsedown" : "1.6.0",
    "jitamin/json-rpc" : "1.2.2",
    "jitamin/picodb" : "1.0.15",
    "jitamin/picofeed": "0.1.25",
    "jitamin/simple-logger" : "1.0.2",
    "jitamin/simple-validator" : "1.0.2",
    "jitamin/simple-queue" : "1.0.1",
    "nesbot/carbon": "~1.21",
    "paragonie/random_compat": "2.0.11",
    "christian-riesen/otp" : "1.4.3",
    "swiftmailer/swiftmailer" : "5.4.5",
    "symfony/console" : "~3.1",
    "symfony/event-dispatcher" : "~3.1",
    "gregwar/captcha": "1.1.1",
    "robmorgan/phinx": "^0.6.5",
    "vlucas/phpdotenv": "^2.4"
    ......

For pimple container and service registration, have look at the code below:

use Jitamin\Foundation\Helper;
use Jitamin\Foundation\Template;
use Pimple\Container;
use Pimple\ServiceProviderInterface;

/**
 * Class of Helper Service Provider.
 */
class HelperServiceProvider implements ServiceProviderInterface
{
    /**
     * Registers services on the given container.
     *
     * @param Container $container
     *
     * @return Container
     */
    public function register(Container $container)
    {
        $container['helper'] = new Helper($container);
        $container['helper']->register('app', '\Jitamin\Helper\AppHelper');
        $container['helper']->register('calendar', '\Jitamin\Helper\CalendarHelper');
        $container['helper']->register('asset', '\Jitamin\Helper\AssetHelper');
        $container['helper']->register('board', '\Jitamin\Helper\BoardHelper');
        $container['helper']->register('dt', '\Jitamin\Helper\DateHelper');
        $container['helper']->register('file', '\Jitamin\Helper\FileHelper');
        $container['helper']->register('form', '\Jitamin\Helper\FormHelper');
        $container['helper']->register('hook', '\Jitamin\Helper\HookHelper');
        $container['helper']->register('ical', '\Jitamin\Helper\ICalHelper');
        $container['helper']->register('layout', '\Jitamin\Helper\LayoutHelper');
        $container['helper']->register('model', '\Jitamin\Helper\ModelHelper');
        $container['helper']->register('subtask', '\Jitamin\Helper\SubtaskHelper');
        $container['helper']->register('task', '\Jitamin\Helper\TaskHelper');
        $container['helper']->register('text', '\Jitamin\Helper\TextHelper');
        $container['helper']->register('url', '\Jitamin\Helper\UrlHelper');
        $container['helper']->register('user', '\Jitamin\Helper\UserHelper');
        $container['helper']->register('avatar', '\Jitamin\Helper\AvatarHelper');
        $container['helper']->register('navbarSearch', '\Jitamin\Helper\NavbarSearchHelper');
        $container['helper']->register('projectRole', '\Jitamin\Helper\ProjectRoleHelper');
        $container['helper']->register('projectHeader', '\Jitamin\Helper\ProjectHeaderHelper');
        $container['helper']->register('projectActivity', '\Jitamin\Helper\ProjectActivityHelper');
        $container['helper']->register('mail', '\Jitamin\Helper\MailHelper');

        $container['template'] = new Template($container['helper']);

        return $container;
    }
}

These codes registered a lot of helper classes in the app’s container, and notice that a layout helper is registered here. It is used for rendering the actual frontend page layout.

class LayoutHelper extends Base
{
    /**
     * Render a template without the layout if Ajax request.
     *
     * @param string $template Template name
     * @param array  $params   Template parameters
     *
     * @return string
     */
    public function app($template, array $params = [])
    {
        $content = $this->template->render($template, $params);

        if ($this->request->isAjax()) {
            return $content;
        }

        return $this->template->render(
            'layouts/master',
            $params + ['content_for_layout' => $content]
        );
    }

    /**
     * Common layout for profile views.
     *
     * @param string $template Template name
     * @param array  $params   Template parameters
     * @param string $subside
     *
     * @return string
     */
    public function profile($template, array $params, $subside = 'profile/_partials/subnav')
    {
        if (isset($params['user'])) {
            $params['title'] = '#'.$params['user']['id'].' '.($params['user']['name'] ?: $params['user']['username']);
        }

        return $this->subLayout('profile/layout', $subside, $template, $params);
    }

    /**
     * Common layout for task views.
     *
     * @param string $template Template name
     * @param array  $params   Template parameters
     *
     * @return string
     */
    public function task($template, array $params)
    {
        $params['page_title'] = $params['task']['project_name'].', #'.$params['task']['id'].' - '.$params['task']['title'];
        $params['title'] = $params['task']['project_name'];

        return $this->subLayout('task/layout', 'task/subside', $template, $params);
    }

    /**
     * Common layout for project views.
     *
     * @param string $template
     * @param array  $params
     * @param string $subside
     *
     * @return string
     */
    public function project($template, array $params, $subside = 'manage/project_settings/subside')
    {
        if (empty($params['title'])) {
            $params['title'] = $params['project']['name'];
        } elseif ($params['project']['name'] !== $params['title']) {
            $params['title'] = $params['project']['name'].' » '.$params['title'];
        }

        return $this->subLayout('project/layout', $subside, $template, $params);
    }

    /**
     * Common layout for project user views.
     *
     * @param string $template
     * @param array  $params
     *
     * @return string
     */
    public function projectUser($template, array $params)
    {
        $params['filter'] = ['user_id' => $params['user_id']];

        return $this->subLayout('manage/project_user_overview/layout', 'manage/project_user_overview/subside', $template, $params);
    }

    /**
     * Common layout for admin views.
     *
     * @param string $template
     * @param array  $params
     * @param string $subside
     *
     * @return string
     */
    public function admin($template, array $params, $subside = 'admin/setting/subside')
    {
        if (!isset($params['values'])) {
            $params['values'] = $this->settingModel->getAll();
        }

        if (!isset($params['errors'])) {
            $params['errors'] = [];
        }

        return $this->subLayout('admin/layout', $subside, $template, $params);
    }

    /**
     * Common layout for dashboard views.
     *
     * @param string $template
     * @param array  $params
     * @param string $subside
     *
     * @return string
     */
    public function dashboard($template, array $params, $subside = 'dashboard/_partials/subnav')
    {
        return $this->subLayout('dashboard/layout', $subside, $template, $params);
    }

    /**
     * Common layout for analytic views.
     *
     * @param string $template
     * @param array  $params
     * @param string $subside
     *
     * @return string
     */
    public function analytic($template, array $params, $subside = 'project/analytic/_partials/subside')
    {
        if (isset($params['project']['name'])) {
            $params['title'] = $params['project']['name'].' » '.$params['title'];
        }

        return $this->subLayout('project/analytic/layout', $subside, $template, $params);
    }

    /**
     * Common method to generate a sub-layout.
     *
     * @param string $sublayout
     * @param string $subside
     * @param string $template
     * @param array  $params
     *
     * @return string
     */
    protected function subLayout($sublayout, $subside, $template, array $params = [])
    {
        $content = $this->template->render($template, $params);

        if ($this->request->isAjax()) {
            return $content;
        }

        $params['content_for_sublayout'] = $content;
        $params['subside_template'] = $subside;

        return $this->app($sublayout, $params);
    }
}

Following the function invoking chain, you will see subLayout() function and you can find the actual page templates under path resources\views\.

For the UI customization, you need to have some knowledge of bower and gulp. Pay attention to gulpfile.js to see how sass and js are compiled.

var src = {
    js: [
        'resources/assets/js/src/Namespace.js',
        'resources/assets/js/src/!(Namespace|Bootstrap|BoardDragAndDrop)*.js',
        'resources/assets/js/src/BoardDragAndDrop.js',
        'resources/assets/js/components/*.js',
        'resources/assets/js/src/Bootstrap.js'
    ]
};
......

gulp.task('js', function() {
    gulp.src(src.js)
        .pipe(concat('app.min.js'))
        .pipe(uglify())
        .pipe(gulp.dest(dist.js))
    ;
});

gulp.task('css', function() {
    gulp.src('resources/assets/sass/*.sass')
        .pipe(sass({outputStyle: 'compressed'}).on('error', sass.logError))
        .pipe(concat('app.min.css'))
        .pipe(gulp.dest(dist.css));
});

gulp.task('default', ['bower', 'vendor', 'js', 'css']);

Now you can jump to resources/assets/js and resources/assets/sass to have a deep dive of JITAMIN’s frontend.

One more thing, as JITAMIN follows MVC design pattern, I’d like to mention that you can find models under /app/Models and routes under /routes.

<?php

/*
 * This file is part of Jitamin.
 *
 * Copyright (C) Jitamin Team
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

return [

// Dashboard routes
'dashboard'          => 'Dashboard/DashboardController@index',
'dashboard/projects' => 'Dashboard/ProjectController@index',
'dashboard/starred'  => 'Dashboard/ProjectController@starred',

'dashboard/tasks'      => 'Dashboard/DashboardController@tasks',
'dashboard/subtasks'   => 'Dashboard/DashboardController@subtasks',
'dashboard/calendar'   => 'Dashboard/DashboardController@calendar',
'dashboard/activities' => 'Dashboard/DashboardController@activities',
'dashboard/slider'     => 'Dashboard/DashboardController@slider',

// Notification routes
'dashboard/notifications'                         => 'Dashboard/NotificationController@index',
'notification/{user_id}/{notification_id}'        => 'Dashboard/NotificationController@redirect',
'notification/{user_id}/{notification_id}/remove' => 'Dashboard/NotificationController@remove',
'notifications/{user_id}/flush'                   => 'Dashboard/NotificationController@flush',

];

For controllers, let’s take Dashboard/DashboardController@index as an example. It is located inside app/Http/Controllers/Dashboard/DashboardController.php.

class DashboardController extends Controller
{
    /**
     * Dashboard overview.
     */
    public function index()
    {
        list($className, $method) = $this->helper->app->getDashboard(true);
        $controllerObject = new $className($this->container);

        return $controllerObject->{$method}();
    }

By following this brief introduction of JITAMIN, you can under its structure easily. Hope it is helpful for you to make your own customization of JITAMIN.

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.