imponeer / smarty-db-resource
Smarty plugin that adds DB resource type
Installs: 20 097
Dependents: 1
Suggesters: 1
Security: 0
Stars: 0
Watchers: 2
Forks: 0
Open Issues: 0
Requires
- php: ^8.3
- ext-pdo: *
- smarty/smarty: ^5
Requires (Dev)
- ext-sqlite3: *
- phpstan/phpstan: ^2
- phpunit/phpunit: ^12
- squizlabs/php_codesniffer: ^3.13
README
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:
- First, look for templates in the specified template set
- If not found, fall back to the default template set
- 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:
- Fork the repository
- Create a feature branch:
git checkout -b feature-name
- Commit your changes:
git commit -am 'Add some feature'
- Push to the branch:
git push origin feature-name
- 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
- Clone the repository
- Install dependencies:
composer install
- Run tests:
composer test
- Check code style:
composer phpcs
- Run static analysis:
composer phpstan