Skip to content

imponeer/smarty-db-resource

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

88 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

License GitHub release PHP Packagist Smarty version requirement

Smarty DB Resource

Database-driven template resource for Smarty

Smarty resource plugin that enables reading templates directly from a database. This powerful extension allows you to store and manage your Smarty templates in a database instead of the filesystem, providing dynamic template management capabilities.

This plugin is inspired by and similar to Xoops - resource.db.

Installation

To install and use this package, we recommend to use Composer:

composer require imponeer/smarty-db-resource

Otherwise, you need to include manually files from src/ directory.

Database Structure

This plugin requires a specific database table structure to store template information. The table should contain the following columns:

Required Columns

Column Name Type Description
Template ID MEDIUMINT UNSIGNED AUTO_INCREMENT Primary key for the template record
Template Set VARCHAR(50) Template set identifier (e.g., 'default', 'theme1')
Template File VARCHAR(50) Template filename (e.g., 'header.tpl', 'footer.tpl')
Template Source TEXT The actual template source code (optional if using file-based templates)
Last Modified INT UNSIGNED Unix timestamp of last modification
Template Description VARCHAR(255) Human-readable description of the template
Last Imported INT UNSIGNED Unix timestamp of last import
Template Type VARCHAR(20) Template type identifier

Example Table Schema

CREATE TABLE `tplfile` (
    `tpl_id` MEDIUMINT UNSIGNED AUTO_INCREMENT,
    `tpl_refid` SMALLINT UNSIGNED NOT NULL DEFAULT '0',
    `tpl_tplset` VARCHAR(50) NOT NULL DEFAULT 'default',
    `tpl_file` VARCHAR(50) NOT NULL DEFAULT '',
    `tpl_desc` VARCHAR(255) NOT NULL DEFAULT '',
    `tpl_lastmodified` INT UNSIGNED NOT NULL DEFAULT '0',
    `tpl_lastimported` INT UNSIGNED NOT NULL DEFAULT '0',
    `tpl_type` VARCHAR(20) NOT NULL DEFAULT '',
    `tpl_source` TEXT,
    PRIMARY KEY (`tpl_id`),
    KEY `tpl_tplset_file` (`tpl_tplset`, `tpl_file`)
);

Note: Column names are configurable when initializing the plugin, so you can adapt the plugin to your existing database schema.

PDO Driver Support

This plugin supports multiple database systems through PDO drivers. The plugin automatically selects the appropriate driver based on your PDO connection:

  • SQLite: Uses optimized SQLite-specific queries
  • All others: Uses MySQL-compatible queries (works with most SQL databases)

Setup

To register the database resource with Smarty, use the registerResource function:

use Imponeer\Smarty\Extensions\DatabaseResource\DatabaseResource;

// Create a Smarty instance
$smarty = new \Smarty\Smarty();

// Create PDO connection
$pdo = new PDO('mysql:host=localhost;dbname=your_database', $username, $password);

// Create and register the database resource
$plugin = new DatabaseResource(
    pdo: $pdo,                                    // PDO database connection
    tplSetName: 'default',                        // Current template set name
    templatesTableName: 'tplfile',                // Table name containing templates
    templateSourceColumnName: 'tpl_source',      // Column containing template source
    templateModificationColumnName: 'tpl_lastmodified', // Column with modification timestamp
    tplSetColumnName: 'tpl_tplset',              // Column identifying template set
    templateNameColumnName: 'tpl_file',          // Column containing template filename
    templatePathGetter: function (array $row): ?string { // Function to get file path from DB row
        return __DIR__ . '/templates/' . $row['tpl_file'];
    },
    defaultTplSetName: 'default'                  // Default template set fallback
);

$smarty->registerResource('db', $plugin);

Custom Database Schema

You can adapt the plugin to your existing database schema by configuring the column names:

$plugin = new DatabaseResource(
    pdo: $pdo,
    tplSetName: 'my_theme',
    templatesTableName: 'custom_templates',       // Your table name
    templateSourceColumnName: 'template_content', // Your source column
    templateModificationColumnName: 'modified_at', // Your timestamp column
    tplSetColumnName: 'theme_name',               // Your template set column
    templateNameColumnName: 'filename',          // Your filename column
    templatePathGetter: function (array $row): ?string {
        // Custom logic for file path resolution
        return '/path/to/templates/' . $row['filename'];
    },
    defaultTplSetName: 'default_theme'
);

Template Path Resolution Examples

The templatePathGetter function allows you to customize how database records are converted to file paths:

// Simple file path concatenation
$templatePathGetter = function (array $row): ?string {
    return __DIR__ . '/templates/' . $row['tpl_file'];
};

// Subdirectory organization by template type
$templatePathGetter = function (array $row): ?string {
    $subdir = $row['tpl_type'] ?? 'default';
    return __DIR__ . '/templates/' . $subdir . '/' . $row['tpl_file'];
};

// Conditional file resolution with validation
$templatePathGetter = function (array $row): ?string {
    if (empty($row['tpl_file'])) {
        return null; // No file path available
    }

    $basePath = __DIR__ . '/templates/';
    $filePath = $basePath . $row['tpl_file'];

    return is_file($filePath) ? $filePath : null;
};

Using the Built-in TemplatePathResolver

The package includes a built-in TemplatePathResolver class that provides a clean, object-oriented alternative to closures for template path resolution:

use Imponeer\Smarty\Extensions\DatabaseResource\Resolver\TemplatePathResolver;

// Create the resolver with your template base path
$templatePathResolver = new TemplatePathResolver('/path/to/templates');

// Use it in DatabaseResource
$plugin = new DatabaseResource(
    pdo: $pdo,
    tplSetName: 'default',
    templatesTableName: 'tplfile',
    templateSourceColumnName: 'tpl_source',
    templateModificationColumnName: 'tpl_lastmodified',
    tplSetColumnName: 'tpl_tplset',
    templateNameColumnName: 'tpl_file',
    templatePathGetter: $templatePathResolver,
    defaultTplSetName: 'default'
);

Custom Template File Column

You can also specify a custom column name for the template filename:

// Use a custom column name for template files
$templatePathResolver = new TemplatePathResolver(
    templateBasePath: '/path/to/templates',
    templateFileColumn: 'custom_filename_column'
);

Using with Symfony Container

To integrate with Symfony, you can leverage autowiring, which is the recommended approach for modern Symfony applications:

# config/services.yaml
services:
    # Enable autowiring and autoconfiguration
    _defaults:
        autowire: true
        autoconfigure: true

    # Register your application's services
    App\:
        resource: '../src/*'
        exclude: '../src/{DependencyInjection,Entity,Tests,Kernel.php}'

    # Configure PDO connection
    PDO:
        arguments:
            $dsn: '%env(DATABASE_URL)%'
            $username: '%env(DB_USERNAME)%'
            $password: '%env(DB_PASSWORD)%'

    # Configure template path resolver
    Imponeer\Smarty\Extensions\DatabaseResource\Resolver\TemplatePathResolver:
        arguments:
            $templateBasePath: '%kernel.project_dir%/templates'

    # Configure DatabaseResource
    Imponeer\Smarty\Extensions\DatabaseResource\DatabaseResource:
        arguments:
            $pdo: '@PDO'
            $tplSetName: 'default'
            $templatesTableName: 'tplfile'
            $templateSourceColumnName: 'tpl_source'
            $templateModificationColumnName: 'tpl_lastmodified'
            $tplSetColumnName: 'tpl_tplset'
            $templateNameColumnName: 'tpl_file'
            $templatePathGetter: '@Imponeer\Smarty\Extensions\DatabaseResource\Resolver\TemplatePathResolver'
            $defaultTplSetName: 'default'

    # Configure Smarty with the extension
    Smarty\Smarty:
        calls:
            - [registerResource, ['db', '@Imponeer\Smarty\Extensions\DatabaseResource\DatabaseResource']]

Then in your application code:

// Get the Smarty instance with the database resource already registered
$smarty = $container->get(\Smarty\Smarty::class);

Using with PHP-DI

With PHP-DI container, you can take advantage of autowiring for a clean configuration:

use function DI\create;
use function DI\get;
use function DI\factory;
use Imponeer\Smarty\Extensions\DatabaseResource\Resolver\TemplatePathResolver;

return [
    // Configure PDO
    PDO::class => factory(function () {
        return new PDO('mysql:host=localhost;dbname=your_database', $username, $password);
    }),

    // Configure TemplatePathResolver
    TemplatePathResolver::class => create()
        ->constructor(__DIR__ . '/templates'),

    // Configure DatabaseResource
    \Imponeer\Smarty\Extensions\DatabaseResource\DatabaseResource::class => create()
        ->constructor(
            get(PDO::class),
            'default',                    // tplSetName
            'tplfile',                   // templatesTableName
            'tpl_source',                // templateSourceColumnName
            'tpl_lastmodified',          // templateModificationColumnName
            'tpl_tplset',                // tplSetColumnName
            'tpl_file',                  // templateNameColumnName
            get(TemplatePathResolver::class), // templatePathGetter
            'default'                    // defaultTplSetName
        ),

    // Configure Smarty with the database resource
    \Smarty\Smarty::class => create()
        ->method('registerResource', 'db', get(\Imponeer\Smarty\Extensions\DatabaseResource\DatabaseResource::class))
];

Then in your application code:

// Get the configured Smarty instance
$smarty = $container->get(\Smarty\Smarty::class);

Using with League Container

If you're using League Container, you can register the extension like this:

use League\Container\Container;
use Imponeer\Smarty\Extensions\DatabaseResource\DatabaseResource;
use Imponeer\Smarty\Extensions\DatabaseResource\Resolver\TemplatePathResolver;

// Create the container
$container = new Container();

// Register PDO
$container->add(PDO::class, function() {
    return new PDO('mysql:host=localhost;dbname=your_database', $username, $password);
});

// Register TemplatePathResolver
$container->add(TemplatePathResolver::class, function() {
    return new TemplatePathResolver(__DIR__ . '/templates');
});

// Register DatabaseResource
$container->add(DatabaseResource::class, function() use ($container) {
    return new DatabaseResource(
        pdo: $container->get(PDO::class),
        tplSetName: 'default',
        templatesTableName: 'tplfile',
        templateSourceColumnName: 'tpl_source',
        templateModificationColumnName: 'tpl_lastmodified',
        tplSetColumnName: 'tpl_tplset',
        templateNameColumnName: 'tpl_file',
        templatePathGetter: $container->get(TemplatePathResolver::class),
        defaultTplSetName: 'default'
    );
});

// Register Smarty with the database resource
$container->add(\Smarty\Smarty::class, function() use ($container) {
    $smarty = new \Smarty\Smarty();
    // Configure Smarty...

    // Register the database resource
    $smarty->registerResource('db', $container->get(DatabaseResource::class));

    return $smarty;
});

Then in your application code:

// Get the configured Smarty instance
$smarty = $container->get(\Smarty\Smarty::class);

Usage

Basic Template Inclusion

To use database-stored templates in your Smarty templates, use the db: prefix when referencing template files:

{* Include a template from the database *}
{include file="db:header.tpl"}

{* Include with subdirectory structure *}
{include file="db:layouts/main.tpl"}

{* Include with variables *}
{include file="db:user/profile.tpl" user=$currentUser}

Template Examples

Main Layout Template

{* File: db:layout.tpl *}
<!DOCTYPE html>
<html>
<head>
    <title>{$pageTitle|default:"My Website"}</title>
    {include file="db:includes/head.tpl"}
</head>
<body>
    {include file="db:includes/header.tpl"}

    <main>
        {$content}
    </main>

    {include file="db:includes/footer.tpl"}
</body>
</html>

Dynamic Content Loading

{* Load different templates based on conditions *}
{if $userType == 'admin'}
    {include file="db:admin/dashboard.tpl"}
{else}
    {include file="db:user/dashboard.tpl"}
{/if}

{* Loop through template sections *}
{foreach $sections as $section}
    {include file="db:sections/{$section.template}" data=$section.data}
{/foreach}

Template Set Management

The plugin supports multiple template sets, allowing you to have different themes or versions:

// Switch to a different template set
$plugin = new DBResource(
    pdo: $pdo,
    tplSetName: 'mobile_theme',  // Use mobile-specific templates
    // ... other parameters
);

Templates are resolved with fallback logic:

  1. First, look for templates in the specified template set
  2. If not found, fall back to the default template set
  3. If still not found, attempt to load from filesystem (if templatePathGetter is configured)

Development

Code Quality Tools

This project uses several tools to ensure code quality:

  • PHPUnit - For unit testing

    composer test
  • PHP CodeSniffer - For coding standards (PSR-12)

    composer phpcs    # Check code style
    composer phpcbf   # Fix code style issues automatically
  • PHPStan - For static analysis

    composer phpstan

Running Tests

The test suite includes comprehensive tests for database operations and template resolution:

# Run all tests
composer test

# Run tests with coverage
vendor/bin/phpunit --coverage-html coverage/

Documentation

API documentation is automatically generated and available in the project's wiki. For more detailed information about the classes and methods, please refer to the project wiki.

Contributing

Contributions are welcome! Here's how you can contribute:

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature-name
  3. Commit your changes: git commit -am 'Add some feature'
  4. Push to the branch: git push origin feature-name
  5. Submit a pull request

Contribution Guidelines

  • Follow PSR-12 coding standards - Use composer phpcs to check your code
  • Write tests - Include unit tests for any new features or bug fixes
  • Update documentation - Update README.md and inline documentation as needed
  • Test thoroughly - Ensure your changes work with all supported database systems

Reporting Issues

If you find a bug or have a feature request, please create an issue in the issue tracker.

When reporting bugs, please include:

  • PHP version
  • Database system and version
  • Smarty version
  • Steps to reproduce the issue
  • Expected vs actual behavior

Development Setup

  1. Clone the repository
  2. Install dependencies: composer install
  3. Run tests: composer test
  4. Check code style: composer phpcs
  5. Run static analysis: composer phpstan

Contributors 5

Languages