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.

CRM на PHP - comments

En
Crm php (php)