Yeni Modül Oluştur

Modül, modules/{name}/ altında yaşayan bağımsız bir PHP sınıfıdır. Modüller tema dosyalarını değiştirmeden çalışır — tüm şablonlar modül klasöründe kalır.

Yapay zeka destekli geliştirme:
Tam geliştirici kılavuzunu indirip herhangi bir yapay zeka asistanına (ChatGPT, Claude, Cursor vb.) yükleyin:

github.com/bera65/frisay/blob/main/modules/MODULE_DEVELOPER_GUIDE.md

Yapay zeka bu dosyayı okuduktan sonra modül mimarisini, adlandırma kurallarını, API kalıplarını, güvenlik gereksinimlerini ve yardımcı sınıfları anlar — böylece modülünüzü sade dille tarif edip çalışan kodu çok daha hızlı üretebilirsiniz.

Bir Modül Ne Yapabilir?

  • Yönetim paneli yapılandırma ekranı (adminPage())
  • Görüntüleme kancaları ile vitrin HTML enjeksiyonu — bkz. Kanca Listesi
  • JSON API uç noktaları (api/{action}.php)
  • $routes ile özel mağaza sayfaları
  • Ödeme yöntemi entegrasyonu (ödeme)
  • install.sql / uninstall.sql ile veritabanı tabloları

Modül Dosya Şeması

modules/{name}/
  {name}.php                          ← Main class (REQUIRED)
  logo.png                            ← Admin module list icon
  install.sql                         ← Install SQL (optional)
  uninstall.sql                       ← Uninstall SQL (optional)
  assets/
    templates/
      admin/
        admin.tpl                     ← Admin configure page
      front/
        {hook_name}.tpl               ← Storefront hook output
    css/
      front.css                       ← Storefront styles
      admin.css                       ← Admin styles
    js/
      front.js                        ← Storefront scripts
      admin.js                        ← Admin scripts
    img/                              ← Images
  api/
    {action}.php                      ← API endpoint files
  front/
    {page}.php                        ← Custom store route file
        

Adlandırma Kuralları (Kritik)

ÖğeKuralÖrnek
Klasör adıKüçük harf, tire kullanılabilirmy-module
Ana dosya{name}/{name}.phpmodules/my-module/my-module.php
PHP sınıfıPascalCase + Modulemy-module → MyModuleModule
Yönetim URL'siAdmin::url() + ADMIN_URI ileAdmin::url('module-my-module')
API URL'siModül yönlendiricisi ile/api/module.php?m=my-module&action=save
$name özelliğiKlasör adıyla birebir eşleşmelipublic string $name = 'my-module';

ModuleBase — Temel Özellikler

ÖzellikAmaç
$displayHooksDesteklenen kancalar: hook_name => description
$defaultDisplayHooksinstall() sırasında otomatik atanan kancalar
$apiActionsaction => api/file.php eşlemesi
$routesslug => front/page.php özel mağaza sayfaları
$frontStylesheets / $frontScriptsassets/ altındaki vitrin CSS/JS dosya adları. Boş = hiçbir şey yüklenmez (dosyaları açıkça listelemelisiniz veya head.assets kullanın)
$adminStylesheets / $adminScriptsYalnızca modül yönetim yapılandırma sayfasındaki varlıklar
$isPaymenttrue → ödeme adımında ödeme yöntemi olarak kaydeder
$paymentMethodIdorders.payment_method içinde saklanan değer
$paysBeforeOrdertrue → sipariş oluşturulmadan önce ödeme (sanal POS)
Varlık yükleme (v2.5.0+): Boş $frontStylesheets / $frontScripts artık assets/css veya assets/js içindeki her dosyayı otomatik yüklemez. İhtiyacınız olan her dosyayı bildirin, örn. ['front.css'].

renderFrontTemplate — Önemli Kural

Değişkenleri doğrudan global Smarty'ye atamak yerine her zaman $this->renderFrontTemplate('hook_name', $vars) kullanın. Bu, izole bir alt şablonda render eder ve $isLoggedIn veya $cart gibi global değişkenleri kirletmez.

  • Şablon yolu: assets/templates/front/{name}.tpl
  • HTML dizesi döndürür — temada {$hooks.footer nofilter} ile çıktılanır

Yönetim Yapılandırması

  • URL: Admin::url('module-{name}') — asla /admin/ yolunu sabit yazmayın (yol config/env.php içindeki ADMIN_URI'den gelir)
  • Şablon: assets/templates/admin/admin.tpl
  • Yönetim görüntüleme kancaları: admin_* kancaları için renderAdminDisplayHook($hook, $context) uygulayın — bkz. Kanca Listesi
  • İsteğe bağlı kenar çubuğu bağlantısı: boot() içinde $this->registerAdminMenuLink('Label', 'catalog', 95)
  • Tüm POST formları $adminToken değerini hash_equals() ile doğrulamak zorundadır

API Uç Noktaları

  • URL: POST /api/module.php?m={name}&action={action}
  • Vitrin API'leri $_SESSION['csrf_token'] doğrulamalıdır — header.tpl içindeki global JS csrfToken kullanın
  • Vitrin AJAX'ında CSRF jetonu olarak yönetim veya cron jetonu kullanmayın
  • /modules/{name}/api/*.php dosyalarına doğrudan erişim engellenir (403) — her zaman api/module.php üzerinden yönlendirin
  • Cron işleri: 'cron' => 'api/cron.php' olarak kaydedin ve /api/module.php?m={name}&action=cron&token=... kullanın

Kurulumdan Sonra

  1. Gerekli dosyalarla modules/{name}/ klasörünü oluşturun
  2. Yönetim → Modüller sayfasına gidin — modül listede görünür
  3. Kur'a tıklayın — defaultDisplayHooks otomatik atanır
  4. Gerekirse modül detay sayfasında kanca atamalarını ayarlayın
  5. Kurulumdan sonra modül varsayılan olarak aktiftir

Güvenlik Kontrol Listesi

  • Dosya koruması: if (!defined('IN_SCRIPT') && !defined('IN_ADMIN')) exit;
  • Yönetim formları: hash_equals($adminToken, $postToken)
  • Mağaza API: oturumdaki csrfToken doğrulayın
  • SQL: her zaman DB::execute($sql, [$param]) ile hazırlanmış ifadeler kullanın
  • Kullanıcı girdisi: trim, strip_tags, Smarty |escape
  • Kanca şablonlarında satır içi <script> kullanmayın — assets/js/ + data-api-url kullanın
  • Tema veya çekirdek dosyalarını değiştirmeyin

Referans Modüller

ModülNe öğrenilir
whatsappBasit alt bilgi kancası + SQL
newsletterAPI + alt bilgi formu + CSRF kalıbı
reviewsÜrün sekmeleri + API + teslimat sonrası davet cron
questionÜrün sekmeleri + giriş gerektiren API + bot koruması
sliderYönetim CRUD + görsel yükleme
bankwireÖdeme + sipariş onay kancası
sanalpospaysBeforeOrder + front route
recaptchaform.captcha.validate + auth/contact görüntüleme kancaları
alertproduct.updated + order.placed + stok gelince formu
abandoned-cartcart.changed takibi + hatırlatma cron
facebook-pixelhead.top / footer / product_detail takibi
instagram-galleryhome_bottom görüntüleme kancası
export-exceladmin_header yönetim görüntüleme kancası
product-setÜrün setleri + product.updated
PHP 7.4 uyumluluğu: match, str_starts_with(), adlandırılmış argümanlar veya birleşik tipler kullanmayın. Bunun yerine switch ve strpos() kullanın.
PHP
<?php

if (!defined('IN_SCRIPT') && !defined('IN_ADMIN')) {
	exit;
}

require_once dirname(__DIR__, 2) . '/core/ModuleBase.php';

class MyModuleModule extends ModuleBase
{
	// ── Identity (REQUIRED) ───────────────────────────────
	public string $name = 'my-module';        // must match folder name
	public string $title = 'My Module';
	public string $version = '1.0.0';
	public string $description = 'Short description';
	public string $author = 'FriSay';

	// ── Display hooks ─────────────────────────────────────
	public array $displayHooks = [
		'footer' => 'Adds HTML to the footer area',
	];
	public array $defaultDisplayHooks = ['footer'];

	// ── CSS/JS (empty = load nothing — list files explicitly) ─
	public array $frontStylesheets = ['front.css'];
	public array $frontScripts = [];
	public array $adminStylesheets = ['admin.css'];
	public array $adminScripts = [];

	// ── API endpoints ─────────────────────────────────────
	public array $apiActions = [
		'save' => 'api/save.php',
	];

	// ── Custom store routes: site.com/{slug} ────────────
	public array $routes = [
		// 'my-page' => 'front/page.php',
	];

	// ── REQUIRED: install / uninstall ─────────────────────
	public function install(): bool
	{
		return $this->runSqlFile('install.sql');
		// No database table: return true;
	}

	public function uninstall(): bool
	{
		return $this->runSqlFile('uninstall.sql');
	}

	// ── Optional: runs when module is loaded ──────────────
	public function boot(): void
	{
		Module::registerHook('order.placed', function (array $order): void {
			// Run after order is created
		});
	}

	// ── Admin configure page (Admin::url('module-my-module')) ────
	public function adminPage(): void
	{
		global $smarty, $adminToken;

		$flash = '';

		if (Tools::isSubmit('saveMyModule')) {
			$postToken = (string) Tools::getValue('token');

			if (hash_equals($adminToken, $postToken)) {
				Settings::set('MY_MODULE_SETTING', trim((string) Tools::getValue('setting')));
				$flash = 'Saved';
			} else {
				$flash = 'Invalid request';
			}
		}

		$smarty->assign([
			'setting' => Settings::get('MY_MODULE_SETTING'),
			'flash'   => $flash,
		]);
	}

	// ── Storefront hook output ────────────────────────────
	public function renderDisplayHook(string $hook, array $context = []): ?string
	{
		if ($hook !== 'footer') {
			return null;
		}

		$html = $this->renderFrontTemplate('footer', [
			'message' => Settings::get('MY_MODULE_SETTING'),
		]);

		return $html !== '' ? $html : null;
	}
}
admin.tpl (Smarty)
{* assets/templates/admin/admin.tpl *}

{if $flash}
<div class="alert alert-info">{$flash|escape}</div>
{/if}

<form method="post" class="admin-panel p-3" style="max-width:400px">
	<input type="hidden" name="saveMyModule" value="1">
	<input type="hidden" name="token" value="{$adminToken}">

	<label class="form-label">Setting value</label>
	<input type="text" name="setting" class="form-control" value="{$setting|escape}">

	<button type="submit" class="btn btn-dark mt-3">Save</button>
</form>
api/save.php — Vitrin API kalıbı
<?php

if (!defined('IN_SCRIPT')) {
	exit;
}

header('Content-Type: application/json; charset=utf-8');

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
	http_response_code(405);
	echo json_encode(['success' => false, 'message' => 'Method not allowed']);
	exit;
}

// Store CSRF — use global csrfToken in JS, NOT admin/cron tokens
$token = Tools::getValue('token') ?: ($_SERVER['HTTP_X_CSRF_TOKEN'] ?? '');

if (!hash_equals($_SESSION['csrf_token'] ?? '', (string) $token)) {
	http_response_code(403);
	echo json_encode(['success' => false, 'message' => 'Invalid request']);
	exit;
}

$result = MyModuleModule::doSomething((string) Tools::getValue('field'));
echo json_encode($result);
front.js — Vitrinden AJAX çağrısı
// csrfToken is defined in header.tpl — do not hardcode tokens in templates
$(document).on('submit', '.my-module-form', function (e) {
	e.preventDefault();
	var $form = $(this);
	$.ajax({
		url: $form.data('api-url'),
		method: 'POST',
		dataType: 'json',
		data: {
			token: typeof csrfToken !== 'undefined' ? csrfToken : '',
			field: $.trim($form.find('[name="field"]').val())
		}
	});
});
Ödeme modülü (havale / banka transferi tipi)
<?php

public bool $isPayment = true;
public string $paymentMethodId = 'bank_transfer';
public string $paymentMethodLabel = 'Bank Transfer';
public bool $paysBeforeOrder = false;

// order_payment.tpl — radio value must match $paymentMethodId
public function processPayment(array $order): array
{
	return [
		'success'  => true,
		'redirect' => '',
		'message'  => '',
	];
}
Yapay zeka istem şablonu — ChatGPT / Claude / Cursor'a yapıştırın
FriSay e-commerce project — write a module.

Rules: follow ALL rules in MODULE_DEVELOPER_GUIDE.md
PHP 7.4 compatible. Extend ModuleBase. Do NOT modify theme or core files.

Module info:
- name (slug): {my-module}
- title: {Display Name}
- description: {what it does}

Features:
- Display hooks: {footer, product_tab, ...}
- API actions: {action => description}
- Payment module: {yes/no}
- Database tables: {schema if any}
- Admin settings: {Settings keys or table fields}

Files to generate:
1. modules/{name}/{name}.php
2. install.sql / uninstall.sql (if needed)
3. assets/templates/admin/admin.tpl
4. assets/templates/front/{hook}.tpl (one per hook)
5. assets/css/, assets/js/ (if needed)
6. api/{action}.php (if needed)
7. front/{page}.php (if route needed)

Reference modules: whatsapp, newsletter, reviews, question, bankwire, sanalpos
Yardımcı sınıflar — yaygın kullanım
<?php

// Settings (use UPPERCASE keys with module prefix)
Settings::get('DOMAIN');          // site URL — NOT 'domain'
Settings::get('SITE_NAME');
Settings::set('MY_MODULE_KEY', 'value');

// Database
DB::getRowSafe('table', 'id = ?', [$id]);
DB::insert('table', ['col' => 'value']);
DB::update('table', ['col' => 'v'], 'id = :where_id', ['where_id' => $id]);
DB::execute('DELETE FROM table WHERE id = ?', [$id]);

// Store data
$product = Product::getById($idProduct);   // includes 'url' field
$images  = Product::getImages($idProduct);
$cart    = Cart::getSummary();
$isFav   = Favorite::isFavorite($idProduct);

// Customer
Customer::isLoggedIn();
Customer::getId();
Customer::getCurrent();

// Tools
Tools::displayPrice(99.90);       // ₺99,90
Tools::createSlug('ana sayfa');   // ana-sayfa
Tools::maskName('John Smith');    // J** S****

// Admin URL
Admin::url('module-my-module');   // https://site.com/admin/module-my-module

// Pagination
$pagination = Pagination::build($total, $page, 30, Admin::url('module-my-module'));

// Mail
Mail::send($to, $subject, $bodyHtml);