PHP CRM: от готовых решений до собственной реализации
Обзор вариантов создания CRM на PHP
Основное эффективное решение: разработка на Laravel
Разработка CRM на Laravel с использованием REST API представляет собой наиболее сбалансированный подход по гибкости, производительности и скорости создания. Laravel предоставляет Eloquent ORM, механизм валидации, ресурсы для форматирования ответов, пагинацию и встроенную поддержку очередей. Это позволяет быстро создать масштабируемую систему управления взаимоотношениями с клиентами, адаптируемую под конкретные бизнес-процессы. Цель: создание гибкой CRM для стартапов и средних компаний, требующих кастомизации.
Пример реализации: создание модели Contact, миграции для таблицы contacts (поля name, email, phone), контроллера с методами CRUD. Код миграции:
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateContactsTable extends Migration
{
public function up()
{
Schema::create('contacts', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->string('phone')->nullable();
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('contacts');
}
}
Контроллер для API:
namespace App\Http\Controllers\Api;
use App\Models\Contact;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Http\Resources\ContactResource;
class ContactController extends Controller
{
public function index()
{
return ContactResource::collection(Contact::paginate());
}
public function store(Request $request)
{
$validated = $request->validate([
'name' => 'required|string|max:255',
'email' => 'required|email|unique:contacts,email',
'phone' => 'nullable|string',
]);
$contact = Contact::create($validated);
return new ContactResource($contact);
}
}
Типичные ошибки и их решения:
- Дублирование email: использовать правило unique и обрабатывать исключение QueryException.
- Отсутствие загрузки отношений: для вложенных заметок применить with('notes') в запросе.
- Неправильная настройка подключения к БД: проверить .env и выполнить php artisan migrate.
- Ошибки маршрутов: использовать php artisan route:list, тестировать через Postman.
Как быстро внедрить CRM на PHP без написания кода с нуля?
Готовая платформа SuiteCRM (форк SugarCRM) позволяет развернуть полнофункциональную CRM за несколько минут. Установка через Composer, настройка через веб-интерфейс, возможность расширения модулями. Цель: быстрое решение для малого бизнеса с минимальными доработками. Случаи использования: компании со стандартными процессами продаж и поддержки клиентов.
composer create-project suitecrm/suitecrm:latest
После установки доступен веб-интерфейс для управления контактами, сделками, задачами. Для добавления пользовательского поля можно использовать встроенный Studio или прямой SQL-запрос.
Проблемы: после установки не загружается страница – проверить права на папки cache и config, требования к версии PHP. Решение: следовать официальной документации.
Как создать CRM с использованием Symfony и Doctrine?
Symfony совместно с Doctrine ORM предоставляет мощный инструментарий для корпоративных систем. Используются Security для аутентификации, API Platform для автоматической генерации REST API. Цель: создание высоконагруженной CRM с строгой архитектурой. Случаи: крупные проекты с множеством сущностей и сложными бизнес-правилами.
Пример сущности Contact с аннотациями:
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: ContactRepository::class)]
class Contact
{
#[ORM\Id, ORM\GeneratedValue, ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 255)]
private ?string $name = null;
#[ORM\Column(length: 255, unique: true)]
private ?string $email = null;
// геттеры и сеттеры
}
Контроллер с выводом списка через Serializer.
Типичные ошибки: неправильные аннотации, отсутствие генерации getter/setter. Решение: использовать Symfony Maker Bundle (php bin/console make:entity).
Когда оправдана разработка CRM на чистом PHP?
Для очень маленьких проектов, прототипов или в учебных целях. Чистый PHP даёт полный контроль, но требует ручной обработки безопасности и структуры. Цель: легковесная CRM для одного пользователя. Пример: простой CRUD с PDO.
$pdo = new PDO('mysql:host=localhost;dbname=crm', 'root', '');
$stmt = $pdo->prepare('SELECT * FROM contacts WHERE id = :id');
$stmt->execute([':id' => $_GET['id']]);
$contact = $stmt->fetch(PDO::FETCH_ASSOC);
Основные ошибки: SQL-инъекции при отсутствии подготовленных выражений, XSS при выводе без экранирования, дублирование кода. Решение: всегда использовать PDO с подготовленными запросами, фильтровать и экранировать вывод, применять шаблонизацию.
Расширенные примеры кода для CRM на PHP
Полноценный REST API на Laravel с пагинацией, фильтрацией и отношениями
Модель Contact с отношением к Note:
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use App\Http\Resources\NoteResource;
class Contact extends Model
{
protected $fillable = ['name', 'email', 'phone'];
public function notes()
{
return $this->hasMany(Note::class);
}
public function toArray()
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'notes' => NoteResource::collection($this->whenLoaded('notes')),
];
}
}
Модель Note:
class Note extends Model
{
protected $fillable = ['contact_id', 'content'];
public function contact()
{
return $this->belongsTo(Contact::class);
}
}
Контроллер с фильтрацией по имени и сортировкой:
namespace App\Http\Controllers\Api;
use App\Models\Contact;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Http\Resources\ContactResource;
class ContactController extends Controller
{
public function index(Request $request)
{
$query = Contact::query();
if ($request->has('name')) {
$query->where('name', 'like', '%'.$request->name.'%');
}
$sortField = $request->get('sort', 'id');
$sortOrder = $request->get('order', 'asc');
$query->orderBy($sortField, $sortOrder);
$contacts = $query->with('notes')->paginate($request->get('per_page', 15));
return ContactResource::collection($contacts);
}
public function store(Request $request)
{
$validated = $request->validate([
'name' => 'required|string|max:255',
'email' => 'required|email|unique:contacts,email',
'phone' => 'nullable|string',
]);
$contact = Contact::create($validated);
if ($request->has('notes')) {
foreach ($request->notes as $noteContent) {
$contact->notes()->create(['content' => $noteContent]);
}
}
return new ContactResource($contact->load('notes'));
}
}
Ресурс ContactResource:
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class ContactResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'phone' => $this->phone,
'notes' => NoteResource::collection($this->whenLoaded('notes')),
'created_at' => $this->created_at,
];
}
}
Ресурс NoteResource:
class NoteResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'content' => $this->content,
'created_at' => $this->created_at,
];
}
}
Маршруты в routes/api.php:
Route::apiResource('contacts', App\Http\Controllers\Api\ContactController::class);
Результат запроса GET /api/contacts?name=Иван&sort=email&order=desc&per_page=5:
{
"data": [
{
"id": 3,
"name": "Иван Петров",
"email": "ivan2@example.com",
"phone": null,
"notes": [],
"created_at": "2025-03-28T10:00:00.000000Z"
},
...
],
"meta": {
"current_page": 1,
"last_page": 2,
"per_page": 5,
"total": 10
}
}
Интеграция с внешним API: отправка приветственного письма через очередь
Создание задачи в очереди:
namespace App\Jobs;
use App\Models\Contact;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Mail;
class SendWelcomeEmail implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $contact;
public function __construct(Contact $contact)
{
$this->contact = $contact;
}
public function handle()
{
Mail::send('emails.welcome', ['contact' => $this->contact], function ($message) {
$message->to($this->contact->email)
->subject('Добро пожаловать в CRM');
});
}
}
Вызов в контроллере после создания:
...
$contact = Contact::create($validated);
SendWelcomeEmail::dispatch($contact);
return new ContactResource($contact);
Необходимо настроить очередь (например, database queue). Типичная ошибка: не запущен worker. Решение: запустить php artisan queue:work.