В этой статье я хотел бы познакомить читателей с архитектурой весьма интересного open-source (GPL3) проекта EspoCRM на примере создания нового модуля для этой системы.
Что такое CRM-система (Customer relationship management), думаю, многие уже давно знают. Особенность данной CRM-системы в том, что она написана как Single Page Application и поэтому довольно «шустрая».
Простой дизайн и современные технологии программирования многим придутся по вкусу, а быстродействие данной CRM-системы приятно удивит. На сайте доступна демо-версия.
Добиться высокой скорости работы помогло кэширование скриптов и шаблонов в Local Storage. Все view вместе с дочерними собираются в один большой HTML, который отображается на экране у пользователя.
Система имеет мощный API, использующий JSON, а веб-интерфейс по сути является API-клиентом.
Система не перегружена функционалом, но имеет все необходимое, а также неплохо настраивается.
Для начала нужно создать рабочую директорию нового модуля (это будет пакет модуля) и поместить её в
Структура каталогов нашего модуля должна быть следующая:
При разработке PM мы должны описать две сущности — Project и ProjectTask. Для этого необходимо создать два JSON-файла со следующим содержанием:
После этого мы должны описать поля и связи для наших сущностей в метаданных
Указываем, что будем использовать контроллер для CRUD. Файл помещаем в директорию
Для работы сущностей необходимо описать их контроллеры.
Помимо определения контроллеров, также необходимо определить и классы для сущностей.
Добавляем имена наших сущностей в глобальный файл локализации
Для перевода полей, выпадающих списков, связей и т.п. в наших сущностях нужно создать отдельные файлы локализации:
После осуществления всех вышеописанных действий переходим в панель Администратора и производим восстановление системы (Menu -> Administration -> Rebuild), а затем обновляем страницу.
Теперь наш модуль Projects по умолчанию является скрытым; для отображения переходим в панель Администратора (Administration -> User Interface) и делаем его видимым. При помощи Field Manager и Layout Manager можно легко добавить новые поля и настроить внешний вид отображения данных.
Я по сути не имею отношения к проекту, я лишь довольный пользователь. Процесс создания модуля описан разработчиками и публикуется с их согласия. И разработчики, и я готовы ответить на все интересующие вопросы.
Что такое CRM-система (Customer relationship management), думаю, многие уже давно знают. Особенность данной CRM-системы в том, что она написана как Single Page Application и поэтому довольно «шустрая».
Простой дизайн и современные технологии программирования многим придутся по вкусу, а быстродействие данной CRM-системы приятно удивит. На сайте доступна демо-версия.
Добиться высокой скорости работы помогло кэширование скриптов и шаблонов в Local Storage. Все view вместе с дочерними собираются в один большой HTML, который отображается на экране у пользователя.
Система имеет мощный API, использующий JSON, а веб-интерфейс по сути является API-клиентом.
Система не перегружена функционалом, но имеет все необходимое, а также неплохо настраивается.
Создание нового модуля
Для начала нужно создать рабочую директорию нового модуля (это будет пакет модуля) и поместить её в
application/Espo/Modules
:application/Espo/Modules/PM
Структура каталогов нашего модуля должна быть следующая:
application/Espo/Modules/PM/Controllers/
application/Espo/Modules/PM/Entities/
application/Espo/Modules/PM/Resources/
Описание метаданных
При разработке PM мы должны описать две сущности — Project и ProjectTask. Для этого необходимо создать два JSON-файла со следующим содержанием:
application/Espo/Modules/PM/Resources/metadata/scopes/Project.json
{
"entity": true,
"layouts": true,
"tab": true,
"acl": true,
"module": "PM",
"customizable": true,
"stream": true,
"importable": true
}
application/Espo/Modules/PM/Resources/metadata/scopes/ProjectTask.json
{
"entity": true,
"layouts": true,
"tab": false,
"acl": true,
"module": "PM",
"customizable": true,
"stream": true,
"importable": true
}
После этого мы должны описать поля и связи для наших сущностей в метаданных
entityDefs
.application/Espo/Modules/PM/Resources/metadata/entityDefs/Project.json
{
"fields": {
"name": {
"type": "varchar",
"required": true
},
"status": {
"type": "enum",
"options": [
"Draft",
"Active",
"Completed",
"On Hold",
"Dropped"
],
"default": "Active"
},
"description": {
"type": "text"
},
"account": {
"type": "link"
},
"createdAt": {
"type": "datetime",
"readOnly": true
},
"modifiedAt": {
"type": "datetime",
"readOnly": true
},
"createdBy": {
"type": "link",
"readOnly": true
},
"modifiedBy": {
"type": "link",
"readOnly": true
},
"assignedUser": {
"type": "link",
"required": true
},
"teams": {
"type": "linkMultiple"
}
},
"links": {
"createdBy": {
"type": "belongsTo",
"entity": "User"
},
"modifiedBy": {
"type": "belongsTo",
"entity": "User"
},
"assignedUser": {
"type": "belongsTo",
"entity": "User"
},
"teams": {
"type": "hasMany",
"entity": "Team",
"relationName": "EntityTeam"
},
"account": {
"type": "belongsTo",
"entity": "Account",
"foreign": "projects"
},
"projectTasks": {
"type": "hasMany",
"entity": "ProjectTask",
"foreign": "project"
}
},
"collection": {
"sortBy": "createdAt",
"asc": false,
"boolFilters": [
"onlyMy"
]
}
}
application/Espo/Modules/PM/Resources/metadata/entityDefs/ProjectTask.json
{
"fields": {
"name": {
"type": "varchar",
"required": true
},
"status": {
"type": "enum",
"options": [
"Not Started",
"Started",
"Completed",
"Canceled"
],
"default": "Not Started"
},
"dateStart": {
"type": "date"
},
"dateEnd": {
"type": "date"
},
"estimatedEffort": {
"type": "float"
},
"actualDuration": {
"type": "float"
},
"description": {
"type": "text"
},
"project": {
"type": "link"
},
"createdAt": {
"type": "datetime",
"readOnly": true
},
"modifiedAt": {
"type": "datetime",
"readOnly": true
},
"createdBy": {
"type": "link",
"readOnly": true
},
"modifiedBy": {
"type": "link",
"readOnly": true
},
"assignedUser": {
"type": "link",
"required": true
},
"teams": {
"type": "linkMultiple"
}
},
"links": {
"createdBy": {
"type": "belongsTo",
"entity": "User"
},
"modifiedBy": {
"type": "belongsTo",
"entity": "User"
},
"assignedUser": {
"type": "belongsTo",
"entity": "User"
},
"teams": {
"type": "hasMany",
"entity": "Team",
"relationName": "EntityTeam"
},
"project": {
"type": "belongsTo",
"entity": "Project",
"foreign": "projectTasks"
}
},
"collection": {
"sortBy": "createdAt",
"asc": false,
"boolFilters": [
"onlyMy"
]
}
}
Указываем, что будем использовать контроллер для CRUD. Файл помещаем в директорию
clientDefs
.application/Espo/Modules/PM/Resources/metadata/clientDefs/Project.json
{
"controller": "Controllers.Record"
}
application/Espo/Modules/PM/Resources/metadata/clientDefs/ProjectTask.json
{
"controller": "Controllers.Record"
}
Контроллеры классов
Для работы сущностей необходимо описать их контроллеры.
application/Espo/Modules/PM/Controllers/Project.php
<?php
namespace Espo\Modules\PM\Controllers;
class Project extends \Espo\Core\Controllers\Record
{
}
application/Espo/Modules/PM/Controllers/ProjectTask.php
<?php
namespace Espo\Modules\PM\Controllers;
class ProjectTask extends \Espo\Core\Controllers\Record
{
}
Классы сущностей
Помимо определения контроллеров, также необходимо определить и классы для сущностей.
application/Espo/Modules/PM/Entities/Project.php
<?php
namespace Espo\Modules\PM\Entities;
class Project extends \Espo\Core\ORM\Entity
{
}
application/Espo/Modules/PM/Entities/ProjectTask.php
<?php
namespace Espo\Modules\PM\Entities;
class ProjectTask extends \Espo\Core\ORM\Entity
{
}
Локализация (I18n)
Добавляем имена наших сущностей в глобальный файл локализации
Global.json
:application/Espo/Modules/PM/Resources/i18n/en_US/Global.json
{
"scopeNames": {
"Project": "Project",
"ProjectTask": "Project Task"
},
"scopeNamesPlural": {
"Project": "Projects",
"ProjectTask": "Project Tasks"
}
}
Для перевода полей, выпадающих списков, связей и т.п. в наших сущностях нужно создать отдельные файлы локализации:
application/Espo/Modules/PM/Resources/i18n/en_US/Project.json
{
"labels": {
"Create Project": "Create Project"
},
"fields": {
"name": "Name",
"status": "Status",
"account": "Account"
},
"links": {
"projectTasks": "Project Tasks"
}
}
application/Espo/Modules/PM/Resources/i18n/en_US/ProjectTask.json
{
"labels": {
"Create ProjectTask": "Create Project Task"
},
"fields": {
"name": "Name",
"status": "Status",
"project": "Project",
"dateStart": "Date Start",
"dateEnd": "Date End",
"estimatedEffort": "Estimated Effort (hrs)",
"actualDuration": "Actual Duration (hrs)"
}
}
Последний штрих
После осуществления всех вышеописанных действий переходим в панель Администратора и производим восстановление системы (Menu -> Administration -> Rebuild), а затем обновляем страницу.
Теперь наш модуль Projects по умолчанию является скрытым; для отображения переходим в панель Администратора (Administration -> User Interface) и делаем его видимым. При помощи Field Manager и Layout Manager можно легко добавить новые поля и настроить внешний вид отображения данных.
Я по сути не имею отношения к проекту, я лишь довольный пользователь. Процесс создания модуля описан разработчиками и публикуется с их согласия. И разработчики, и я готовы ответить на все интересующие вопросы.