Создание собственной панели управления сайтом: подходы и примеры на PHP

Раздел: Веб-разработка -> Разработка веб-приложений

Реализация панели управления сайтом на PHP

Для создания системы управления сайтом (CMS) на PHP чаще всего применяют архитектуру MVC, маршрутизацию, работу с базой данных через PDO и шаблонизатор. Ниже представлено основное решение, а затем альтернативные подходы.

Основной подход: самописная MVC-админка

Как создать собственную панель управления с нуля, чтобы гибко контролировать логику?

Построим простой роутер, контроллер и модель для работы с записями блога. База данных MySQL, таблица posts (id, title, content, created_at).


// index.php - точка входа
require_once 'Router.php';

$router = new Router();
$router->add('GET', '/admin/posts', 'PostController@index');
$router->add('GET', '/admin/posts/create', 'PostController@create');
$router->add('POST', '/admin/posts/store', 'PostController@store');
$router->dispatch($_SERVER['REQUEST_METHOD'], parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH));
  

разработка web приложений php (разработка веб-приложений на php)


// Router.php
class Router {
    private $routes = [];

    public function add($method, $path, $handler) {
        $this->routes[] = compact('method', 'path', 'handler');
    }

    public function dispatch($method, $uri) {
        foreach ($this->routes as $route) {
            if ($route['method'] === $method && $route['path'] === $uri) {
                list($controller, $action) = explode('@', $route['handler']);
                $obj = new $controller();
                return $obj->$action();
            }
        }
        http_response_code(404);
        echo '404 Not Found';
    }
}
  

управление сайтом php (управление сайтом на php)


// PostController.php
class PostController {
    public function index() {
        $posts = Post::all();
        include 'views/admin/posts/index.php';
    }
    public function create() {
        include 'views/admin/posts/create.php';
    }
    public function store() {
        $data = $_POST;
        Post::create($data);
        header('Location: /admin/posts');
    }
}
  

Php веб интерфейс (создание веб-интерфейса на php)


// Post.php (модель)
class Post {
    public static function all() {
        $db = new PDO('mysql:host=localhost;dbname=test', 'root', '');
        $stmt = $db->query('SELECT * FROM posts ORDER BY created_at DESC');
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }
    public static function create($data) {
        $db = new PDO('mysql:host=localhost;dbname=test', 'root', '');
        $stmt = $db->prepare('INSERT INTO posts (title, content) VALUES (:title, :content)');
        $stmt->execute(['title' => $data['title'], 'content' => $data['content']]);
    }
}
  

Частые ошибки:

  • Отсутствие проверки входящих данных (SQL-инъекции). Решение: использовать подготовленные запросы (как в примере).
  • Неверный путь к файлам представлений. Рекомендуется использовать абсолютные пути или константу __DIR__.
  • Проблемы с сессиями при авторизации. Всегда вызывайте session_start() в начале скрипта.

Вариант 1: Микрофреймворк Slim для быстрого API

Как быстро создать REST API для управления контентом без написания роутера вручную?

Slim предоставляет готовую маршрутизацию, middleware и контейнер зависимостей. Установка через composer: composer require slim/slim. Пример маршрута:


use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Factory\AppFactory;

require __DIR__ . '/vendor/autoload.php';

$app = AppFactory::create();

$app->get('/api/posts', function (Request $request, Response $response) {
    $db = new PDO('mysql:host=localhost;dbname=test', 'root', '');
    $stmt = $db->query('SELECT * FROM posts');
    $posts = $stmt->fetchAll(PDO::FETCH_ASSOC);
    $response->getBody()->write(json_encode($posts));
    return $response->withHeader('Content-Type', 'application/json');
});

$app->run();
  

Возможные проблемы:

  • Не настроен веб-сервер для перенаправления на index.php. В Apache нужен .htaccess с RewriteRule.
  • Ошибки автозагрузки классов. Проверьте namespace и пути.

Вариант 2: Готовая админ-панель Orchid (на Laravel)

Как внедрить готовую панель управления с минимальным кодом?

Orchid для Laravel добавляет CRUD-интерфейс, систему ролей, медиа-менеджер. Установка:


composer require orchid/platform
php artisan orchid:install
php artisan serve
  

После установки создаются ресурсы (Screens) для управления сущностями. Пример:


// app/Orchid/Screens/PostScreen.php
use Orchid\Screen\Screen;
use Orchid\Screen\Actions\Link;

class PostScreen extends Screen {
    public function query(): iterable {
        return ['posts' => Post::paginate()];
    }
    public function layout(): iterable {
        return [
            Layout::table('posts', [
                'title' => 'Заголовок',
                'actions' => Link::make('Редактировать')->route('platform.post.edit'),
            ])
        ];
    }
}
  

Ошибки при внедрении:

  • Конфликт с существующим Laravel-приложением. Следуйте официальной документации по миграции.
  • Проблемы с кэшем. Выполните php artisan optimize:clear.

Вариант 3: Управление через файлы (без базы данных)

Как организовать простой сайт-визитку без SQL?

Контент хранится в JSON-файлах. Пример файла pages/home.json:


{
  "title": "Главная",
  "content": "

Приветствие

" }

Чтение и запись:


function getPage($slug) {
    $file = __DIR__ . "/pages/{$slug}.json";
    if (!file_exists($file)) return null;
    return json_decode(file_get_contents($file), true);
}

function updatePage($slug, $data) {
    file_put_contents(__DIR__ . "/pages/{$slug}.json", json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
}
  

Типичные проблемы:

  • Права доступа к файлам. Необходимо установить chmod 755 или 644.
  • Конфликты одновременной записи. Для промышленного использования не подходит; требуется блокировка flock.

Расширенные примеры реализации администрирования

Полный CRUD с аутентификацией и постраничной навигацией

Пример для самописной системы с использованием PDO и простой сессионной авторизацией.

Пример

// config.php
session_start();
define('DB_HOST', 'localhost');
define('DB_NAME', 'cms');
define('DB_USER', 'root');
define('DB_PASS', '');

function db() {
    static $pdo = null;
    if ($pdo === null) {
        $pdo = new PDO("mysql:host=" . DB_HOST . ";dbname=" . DB_NAME . ";charset=utf8", DB_USER, DB_PASS);
        $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    }
    return $pdo;
}
Пример

// auth.php
class Auth {
    public static function login($username, $password) {
        $stmt = db()->prepare('SELECT * FROM users WHERE username = ?');
        $stmt->execute([$username]);
        $user = $stmt->fetch(PDO::FETCH_ASSOC);
        if ($user && password_verify($password, $user['password'])) {
            $_SESSION['user_id'] = $user['id'];
            return true;
        }
        return false;
    }
    
    public static function check() {
        return isset($_SESSION['user_id']);
    }
    
    public static function logout() {
        session_destroy();
    }
}
Пример

// index.php (роутер с middleware)
require_once 'config.php';
require_once 'auth.php';

$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$method = $_SERVER['REQUEST_METHOD'];

// Простая маршрутизация
if ($path === '/admin/login' && $method === 'GET') {
    include 'views/admin/login.php';
    exit;
}
if ($path === '/admin/login' && $method === 'POST') {
    if (Auth::login($_POST['username'], $_POST['password'])) {
        header('Location: /admin/posts');
        exit;
    }
    header('Location: /admin/login?error=1');
    exit;
}

// Все остальные маршруты требуют авторизации
if (!Auth::check()) {
    header('Location: /admin/login');
    exit;
}

if ($path === '/admin/posts') {
    // Пагинация
    $page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
    $perPage = 5;
    $offset = ($page - 1) * $perPage;
    
    $totalStmt = db()->query('SELECT COUNT(*) FROM posts');
    $total = $totalStmt->fetchColumn();
    $pages = ceil($total / $perPage);
    
    $stmt = db()->prepare('SELECT * FROM posts ORDER BY created_at DESC LIMIT :limit OFFSET :offset');
    $stmt->bindValue(':limit', $perPage, PDO::PARAM_INT);
    $stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
    $stmt->execute();
    $posts = $stmt->fetchAll(PDO::FETCH_ASSOC);
    
    include 'views/admin/posts/index.php';
    exit;
}
Пример

// views/admin/posts/index.php
<h2>Список записей</h2>
<table>
    <tr><th>Заголовок</th><th>Дата</th><th>Действия</th></tr>
    <?php foreach ($posts as $post): ?>
    <tr>
        <td><?= htmlspecialchars($post['title']) ?></td>
        <td><?= $post['created_at'] ?></td>
        <td>
            <a href="/admin/posts/edit/<?= $post['id'] ?>">Редактировать</a>
            <a href="/admin/posts/delete/<?= $post['id'] ?>" onclick="return confirm('Удалить?')">Удалить</a>
        </td>
    </tr>
    <?php endforeach; ?>
</table>
<div class="pagination">
    <?php for ($i = 1; $i <= $pages; $i++): ?>
        <a href="?page=<?= $i ?>" class="<?= $i == $page ? 'active' : '' ?>"><?= $i ?></a>
    <?php endfor; ?>
</div>

Результат работы:

  • После входа отображается таблица с записями, по 5 на страницу.
  • Ссылки пагинации переключают страницы.
  • Заголовки экранированы от XSS.
(Сгенерированный HTML: таблица с двумя строками записи, страницы 1,2,3)

Работа с изображениями: загрузка и удаление

Пример

// upload.php
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['image'])) {
    $targetDir = __DIR__ . '/uploads/';
    $filename = uniqid() . '_' . basename($_FILES['image']['name']);
    $targetFile = $targetDir . $filename;
    
    // Проверка типа файла
    $allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
    if (!in_array($_FILES['image']['type'], $allowedTypes)) {
        echo 'Разрешены только JPEG, PNG, GIF';
        exit;
    }
    
    if (move_uploaded_file($_FILES['image']['tmp_name'], $targetFile)) {
        // Сохраняем путь в БД
        $stmt = db()->prepare('INSERT INTO media (filename, path) VALUES (?, ?)');
        $stmt->execute([$filename, 'uploads/' . $filename]);
        echo 'Файл загружен: ' . $filename;
    } else {
        echo 'Ошибка загрузки';
    }
}
Файл загружен: 67543abc_photo.jpg

Экспорт данных в CSV

Пример

// export.php
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename="posts.csv"');

$output = fopen('php://output', 'w');
fwrite($output, "\xEF\xBB\xBF"); // BOM для UTF-8 в Excel
fputcsv($output, ['ID', 'Заголовок', 'Контент', 'Дата']);

$stmt = db()->query('SELECT id, title, content, created_at FROM posts');
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
    fputcsv($output, $row);
}
fclose($output);
Скачивается файл posts.csv с колонками: ID, Заголовок, Контент, Дата и строками данных.

Управление сайтом на PHP - comments

En
управление сайтом php (php)