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.