Ako klijent treba uređivati nekoliko usluga, blog objave, reference, fotografije i osnovna SEO polja, ne mora uvijek trebati univerzalni CMS s page builderom, pluginovima i velikim administracijskim slojem. Ponekad je bolji custom CMS koji radi manje stvari, ali ih radi jasno, brzo i sigurno.
1. Prvo definirajte što CMS stvarno mora uređivati
Najveća greška u izradi custom CMS-a je krenuti od tehnologije umjesto od sadržaja. Prije baze, admin panela i klasa treba znati koje entitete klijent stvarno treba uređivati. Mala poslovna stranica obično ima statične stranice, usluge, blog objave, reference, slike i osnovne globalne postavke.
Ne treba odmah graditi sustav za sve moguće scenarije. Bolji pristup je napraviti stabilnu jezgru i dodavati module samo kada postoji stvarna potreba. CMS po mjeri ima smisla upravo zato što se ne mora ponašati kao univerzalna platforma.
2. Minimalna struktura baze
Za jednostavan CMS ne treba previše tablica, ali treba izbjeći i jednu ogromnu tablicu koja sadrži sve. Dobar početak je imati tablice za korisnike, stranice, blog objave i media assete. Ako projekt ima više jezika, verzioniranje ili složen workflow, struktura se širi.
CREATE TABLE pages (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(180) NOT NULL,
slug VARCHAR(180) NOT NULL UNIQUE,
meta_title VARCHAR(180) NULL,
meta_description VARCHAR(255) NULL,
excerpt TEXT NULL,
body MEDIUMTEXT NOT NULL,
hero_image_id INT UNSIGNED NULL,
status ENUM('draft', 'published') NOT NULL DEFAULT 'draft',
published_at DATETIME NULL,
created_at DATETIME NOT NULL,
updated_at DATETIME NOT NULL
);
Ovdje su SEO polja dio sadržajnog modela, a ne naknadna misao. Ako urednik može promijeniti naslov stranice, treba moći promijeniti i meta title, opis, excerpt i sliku za dijeljenje.
3. Slug i canonical moraju biti pod kontrolom
CMS treba omogućiti uređivanje sluga, ali ne smije ga mijenjati automatski svaki put kada se promijeni naslov. Jednom kada URL uđe u sitemap, interne linkove i Google indeks, slug postaje stabilan identitet stranice. Ako se mijenja, treba 301 redirect.
<?php
final readonly class CmsPage
{
public function __construct(
public int $id,
public string $title,
public string $slug,
public string $status,
public ?string $metaTitle,
public ?string $metaDescription,
) {}
public function canonicalUrl(string $baseUrl): string
{
return rtrim($baseUrl, '/') . '/' . trim($this->slug, '/');
}
public function isPublished(): bool
{
return $this->status === 'published';
}
}
Za URL disciplinu pogledajte i vodič kako strukturirati URL-ove za SEO, jer CMS treba proizvoditi URL-ove koji dugoročno ostaju stabilni.
4. Repository sloj drži SQL izvan templatea
Template ne bi trebao znati kako se dohvaća stranica iz baze. Ako se SQL nalazi direktno u view datoteci, s vremenom je teže testirati, održavati i mijenjati logiku. Jednostavan repository sloj dovoljan je da CMS ostane čitljiv.
<?php
final class PageRepository
{
public function __construct(private PDO $db) {}
public function findPublishedBySlug(string $slug): ?array
{
$stmt = $this->db->prepare(
'SELECT id, title, slug, meta_title, meta_description, body
FROM pages
WHERE slug = :slug AND status = :status
LIMIT 1'
);
$stmt->execute([
'slug' => $slug,
'status' => 'published',
]);
$page = $stmt->fetch(PDO::FETCH_ASSOC);
return $page ?: null;
}
}
5. Draft i published workflow
Čak i mali CMS treba razlikovati draft i published sadržaj. Urednik treba moći pripremiti stranicu bez da je odmah javna. Javni router treba prikazivati samo objavljene stranice, dok admin može vidjeti draftove. Preview opcija je korisna, ali treba paziti da preview URL ne postane indeksiran.
Workflow pravila
- Draft stranice ne smiju biti u sitemapu.
- Draft stranice ne smiju biti dostupne javno bez autorizacije.
- Published stranice moraju imati canonical URL.
- Promjena sluga na published stranici treba ponuditi 301 redirect.
- Brisanje published stranice treba imati odluku: redirect, 404 ili 410.
6. SEO polja su dio CMS-a, ne plugin
Kod CMS-a po mjeri nema potrebe za SEO pluginom ako znate koja polja trebate. Meta title, meta description, canonical URL, OG/Twitter slika, alt tekst, excerpt i schema tip mogu biti dio osnovnog modela. Time urednik dobiva kontrolu, a developer zadržava dosljedan output.
<?php
function page_meta(array $page): array
{
return [
'title' => $page['meta_title'] ?: $page['title'],
'description' => $page['meta_description'] ?: $page['excerpt'] ?? '',
'canonical' => canonical_url('/' . $page['slug']),
'image' => $page['hero_image'] ?? '/assets/images/default-og.webp',
];
}
Ovakva fallback logika je mala, ali važna. CMS ne smije završiti s praznim title tagom, nepostojećim opisom ili pogrešnom slikom za dijeljenje.
7. Media library i slike
Ako CMS dopušta upload slika, mora ih kontrolirati. Originalne slike često dolaze prevelike, u krivom formatu ili bez alt teksta. Dobar CMS treba spremiti original ako treba, ali javnom webu servirati optimizirane izvedenice.
CREATE TABLE media_assets (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
filename VARCHAR(255) NOT NULL,
path VARCHAR(255) NOT NULL,
mime_type VARCHAR(80) NOT NULL,
width INT UNSIGNED NOT NULL,
height INT UNSIGNED NOT NULL,
alt_text VARCHAR(255) NULL,
created_at DATETIME NOT NULL
);
Za detalje oko formata, dimenzija i lazy loadinga, povezano je korisno pročitati optimizaciju slika za web.
8. Admin forme: validacija i CSRF
Admin sučelje je sigurnosno osjetljivije od javnog dijela weba. Svaka forma treba CSRF token, server-side validaciju i provjeru autorizacije. Ako CMS ima upload slika, treba provjeriti MIME tip, veličinu, dimenzije i ekstenziju.
<?php
function require_valid_csrf(array $post, array $session): void
{
$token = (string) ($post['csrf_token'] ?? '');
$expected = (string) ($session['csrf_token'] ?? '');
if ($token === '' || !hash_equals($expected, $token)) {
http_response_code(403);
exit('Invalid CSRF token');
}
}
Sigurnosni principi su slični kao kod kontakt forme. Ako vas zanima taj dio detaljnije, pogledajte članak sigurna PHP kontakt forma preko SMTP-a.
9. Role i prava pristupa
Mali CMS često ima samo jednog administratora, ali čak i tada treba imati jasnu autorizaciju. Ako kasnije dodate urednika, bolje je da sustav već ima pojam role nego da se prava pristupa improviziraju po handlerima.
10. Sitemap i cache invalidacija
Kada CMS objavi novu javnu stranicu, sitemap treba dobiti novi URL. Kada se sadržajno promijeni
postojeća stranica, lastmod treba odražavati stvarnu promjenu. Ako se mijenja slika,
CSS ili JS, treba razmišljati i o cache-busting verziji asseta.
<?php
function public_sitemap_pages(PDO $db): array
{
$stmt = $db->query(
"SELECT slug, updated_at
FROM pages
WHERE status = 'published'
ORDER BY updated_at DESC"
);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
11. Kada custom CMS ima smisla, a kada ne
Custom CMS ima smisla kada projekt ima jasne i ograničene potrebe, kada je bitna brzina, SEO kontrola, sigurnost i jednostavno održavanje. Nema smisla ako klijent treba kompleksan marketplace pluginova, veliki urednički tim, napredno verzioniranje, workflow odobravanja ili funkcionalnosti koje već dobro rješava postojeća platforma.
Zaključak: dobar custom CMS je mali, jasan i namjenski
Custom CMS za malu poslovnu web stranicu treba biti dovoljno jednostavan da ga klijent razumije i dovoljno discipliniran da developer zna gdje je svaka odgovornost. Stranice, SEO polja, slike, draft/published workflow, sigurnost i sitemap čine jezgru koja pokriva većinu stvarnih potreba poslovnog weba.
Ako je CMS dobro osmišljen, on ne usporava web i ne zaključava projekt u nepotrebnu kompleksnost. Naprotiv, postaje stabilan alat za rast sadržaja, SEO autoriteta i dugoročno održavanje. Za širu sliku custom pristupa pogledajte i custom web rješenja vs WordPress te PHP vs .NET za poslovne web stranice.