Skip to content

Latest commit

 

History

53 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

MFragment - Structured HTML Generation for REDAXO

REDAXO Version PHP Version License: MIT

Programmatic HTML Structures for Modern REDAXO Websites

MFragment is a REDAXO addon for structured HTML generation with component-oriented architecture. It enables programmatic creation of HTML structures, responsive media management and Bootstrap 5 integration.

Key Features

Structured HTML Generation

  • Programmatic HTML Creation - Create HTML structures with PHP code
  • Direct HTML Rendering - No template engine required
  • Bootstrap 5 Integration - Includes standard Bootstrap components
  • Method Chaining - Fluent API design for improved developer experience

Create Custom Components

  • Extensible Architecture - Custom components through AbstractComponent inheritance
  • MFragment Integration - All MFragment tools available
  • HTML Element Creation - Create arbitrary HTML structures with MFragment methods
  • Modular Structure - Components can be used in all contexts

HTML Structure Generation

As MForm is for forms, MFragment is for HTML structures - create HTML layouts programmatically:

  • Complete HTML Coverage - Generation of arbitrary HTML elements, attributes and structures
  • Nested Components - Complex layouts with unlimited nesting depth
  • Dynamic Content Generation - Generate HTML programmatically based on data
  • Layout Systems - From simple divs to complex grid systems and components
  • Template Alternative - Replace static templates with dynamic PHP structures

Responsive Media System

  • 360 Media Manager Types - Complete responsive image system
  • 4 Image Series - small, half, full, hero for every use case
  • Automatic WebP Conversion - 25-35% smaller files for better performance
  • Bootstrap 5 Breakpoints - Perfect integration with modern grid systems
  • Hero Series - Full-screen images up to 1920px for modern websites

Performance Characteristics

  • Direct HTML Rendering - No template engine required
  • Performance Monitoring - Render times measurable
  • Database Query Optimization - Request-local caching for Media Manager Types
  • Memory Efficient - Low resource consumption

Developer Experience

  • Documented APIs - Well-documented interfaces with type hints
  • Debug Mode - Detailed development information
  • IDE Support - Complete type hints and documentation
  • Extensible - Easy creation of custom components

Installation

Via REDAXO Installer

  1. Go to System → Packages
  2. Search for "MFragment"
  3. Click Install

Manual Installation

  1. Download the latest version from GitHub
  2. Extract to redaxo/src/addons/mfragment/
  3. Install via REDAXO backend

Media Manager Types (Recommended)

Install the comprehensive responsive media system:

-- Import via REDAXO SQL Import
source install/responsive_complete_system.sql

This adds 360 responsive Media Manager types with automatic WebP conversion.

Quick Start

Basic Component Usage

<?php
use FriendsOfRedaxo\MFragment\Components\Bootstrap\Card;
use FriendsOfRedaxo\MFragment\Components\Default\Figure;

// Create a Bootstrap Card with responsive image
$card = Card::factory()
    ->setHeader('Welcome to MFragment')
    ->setBody('Build modern websites with component architecture.')
    ->setImage('hero-image.jpg', 'full_16x9')
    ->addClass('shadow-sm');

echo $card->show();

Responsive Image Helper

<?php
use FriendsOfRedaxo\MFragment\Components\Default\Figure;

// Generate responsive picture element
$responsiveImage = Figure::factory()
    ->setMedia('hero-background.jpg')
    ->setMediaManagerType('hero_16x9')  // Use Hero series for fullscreen
    ->enableAutoResponsive()
    ->addClass('hero-bg');

echo $responsiveImage->show();

Advanced Component Examples

<?php
use FriendsOfRedaxo\MFragment\Components\Bootstrap\Carousel;
use FriendsOfRedaxo\MFragment\Components\Default\Figure;

$carousel = Carousel::factory('hero-carousel')
    ->addSlide(
        Figure::factory()
            ->setMedia('slide1.jpg')
            ->setMediaManagerType('hero_21x9')
            ->setCaption('Modern Web Development')
            ->addClass('carousel-image')
    )
    ->addSlide(
        Figure::factory()
            ->setMedia('slide2.jpg')
            ->setMediaManagerType('hero_21x9')
            ->setCaption('Responsive Design')
    )
    ->setControls(true)
    ->setIndicators(true)
    ->setAutoplay(5000);

echo $carousel->show();

Available Components

Bootstrap 5 Components

  • Card - Content cards with images, headers and actions
  • Carousel - Image and content sliders
  • Modal - Overlay dialogs and lightboxes
  • Accordion - Collapsible content sections
  • Tabs - Tab navigation for content
  • Alert - Notifications and messages
  • Badge - Status indicators and labels
  • Progress - Progress bars and loading indicators
  • Collapse - Expandable content areas

Default Components

  • Figure - Images with captions and responsive behavior
  • HTMLElement - Generic HTML elements with attribute management
  • ListElement - Ordered and unordered lists
  • Table - Data tables with responsive features

Responsive Media System

Image Series Overview

Series Breakpoints Usage Examples
small 320-768px Thumbnails, Icons Avatars, small previews
half 320-1400px Content images Article images, galleries
full 320-1400px Large content Hero areas, main images
hero 768-1920px Fullscreen areas Headers, landing pages

Supported Aspect Ratios

  • 1:1 - Square images (avatars, thumbnails)
  • 4:3 - Standard photos (content images)
  • 16:9 - Video format (hero areas, media)
  • 21:9 - Cinema format (fullscreen headers)
  • 3:2 - Photography standard
  • 5:2 - Wide banners

Usage Examples

// Hero header with video aspect ratio
rex_media_manager::getUrl('hero_16x9_max_1920', 'header-bg.jpg')

// Content image for articles
rex_media_manager::getUrl('half_4x3_768', 'article-image.jpg')

// Small thumbnail
rex_media_manager::getUrl('small_1x1_320', 'avatar.jpg')

// Fullscreen cinema format
rex_media_manager::getUrl('hero_21x9_max_1920', 'cinema-bg.jpg')

Creating Custom Components

Basic Principle

MFragment allows you to create arbitrary HTML structures using the built-in tools. You can develop custom components that integrate seamlessly into the system and use all MFragment features.

Simple Custom Component

<?php
namespace App\Components;

use FriendsOfRedaxo\MFragment\Components\AbstractComponent;

class MyComponent extends AbstractComponent
{
    protected string $title = '';
    protected string $content = '';
    
    public function setTitle(string $title): self
    {
        $this->title = $title;
        return $this;
    }
    
    public function setContent(string $content): self
    {
        $this->content = $content;
        return $this;
    }
    
    protected function renderHtml(): string
    {
        return '<div' . $this->buildAttributesString() . '>
            <h2>' . htmlspecialchars($this->title) . '</h2>
            <div class="content">' . $this->content . '</div>
        </div>';
    }
}

Advanced Component with Nested Elements

<?php
namespace App\Components;

use FriendsOfRedaxo\MFragment\Components\AbstractComponent;
use FriendsOfRedaxo\MFragment\Components\Default\HTMLElement;
use FriendsOfRedaxo\MFragment\Components\Default\Figure;

class ProductCard extends AbstractComponent
{
    private string $productName = '';
    private string $price = '';
    private string $imageUrl = '';
    private array $features = [];
    
    public function setProductName(string $name): self
    {
        $this->productName = $name;
        return $this;
    }
    
    public function setPrice(string $price): self
    {
        $this->price = $price;
        return $this;
    }
    
    public function setImage(string $imageUrl): self
    {
        $this->imageUrl = $imageUrl;
        return $this;
    }
    
    public function addFeature(string $label, string $value): self
    {
        $this->features[] = ['label' => $label, 'value' => $value];
        return $this;
    }
    
    protected function renderHtml(): string
    {
        // Use other MFragment components
        $image = Figure::factory()
            ->setMedia($this->imageUrl)
            ->setMediaManagerType('half_4x3')
            ->enableLazyLoading()
            ->addClass('product-image');
            
        $features = '';
        foreach ($this->features as $feature) {
            $features .= '<li><strong>' . htmlspecialchars($feature['label']) . ':</strong> ' . htmlspecialchars($feature['value']) . '</li>';
        }
        
        return '<div' . $this->buildAttributesString() . '>
            ' . $image->show() . '
            <div class="product-info">
                <h3>' . htmlspecialchars($this->productName) . '</h3>
                <div class="price">' . htmlspecialchars($this->price) . '</div>
                ' . ($features ? '<ul class="features">' . $features . '</ul>' : '') . '
            </div>
        </div>';
    }
}

Component with MFragment Container

<?php
namespace App\Components;

use FriendsOfRedaxo\MFragment;
use FriendsOfRedaxo\MFragment\Components\AbstractComponent;
use FriendsOfRedaxo\MFragment\Components\Bootstrap\Card;
use FriendsOfRedaxo\MFragment\Components\Default\HTMLElement;

class Dashboard extends AbstractComponent
{
    private array $cards = [];
    
    public function addCard(string $title, string $content, string $icon = ''): self
    {
        $this->cards[] = [
            'title' => $title,
            'content' => $content,
            'icon' => $icon
        ];
        return $this;
    }
    
    protected function renderHtml(): string
    {
        $mfragment = MFragment::factory();
        
        // Container for dashboard
        $container = HTMLElement::factory('div')
            ->addClass('dashboard-grid');
            
        // Add each card as Bootstrap Card
        foreach ($this->cards as $card) {
            $cardComponent = Card::factory()
                ->setHeader($card['title'])
                ->setBody($card['content'])
                ->addClass('dashboard-card');
                
            if ($card['icon']) {
                $cardComponent->prependContent('<i class="' . $card['icon'] . '"></i>');
            }
            
            $mfragment->addComponent($cardComponent);
        }
        
        $container->setContent($mfragment->show());
        
        return '<div' . $this->buildAttributesString() . '>' . 
               $container->show() . 
               '</div>';
    }
}

Using Components

// Simple usage
$myComponent = MyComponent::factory()
    ->setTitle('My Title')
    ->setContent('My Content')
    ->addClass('custom-style');

echo $myComponent->show();

// Advanced product card
$productCard = ProductCard::factory()
    ->setProductName('Gaming Laptop')
    ->setPrice('€ 1,299.00')
    ->setImage('laptop.jpg')
    ->addFeature('Processor', 'Intel Core i7')
    ->addFeature('RAM', '16 GB DDR4')
    ->addFeature('Graphics', 'NVIDIA RTX 3060')
    ->addClass('product-card shadow');

echo $productCard->show();

// Dashboard with multiple cards
$dashboard = Dashboard::factory()
    ->addCard('Users', '1,234 active users', 'fas fa-users')
    ->addCard('Revenue', '€ 45,678.90', 'fas fa-chart-line')
    ->addCard('Orders', '89 new orders', 'fas fa-shopping-cart')
    ->addClass('admin-dashboard');

echo $dashboard->show();

Integration in MFragment

Custom components can be used in all MFragment contexts:

$mfragment = MFragment::factory()
    ->addComponent($myComponent)
    ->addCard(Card::factory()->setHeader('Standard Card'))
    ->addComponent($productCard)
    ->addDiv('Additional content');

echo $mfragment->show();

Building HTML Structures

MFragment is for HTML what MForm is for forms - create HTML layouts programmatically:

Complex Layout Example

// Create a complete article layout
$article = MFragment::factory()
    ->addDiv(
        MFragment::factory()
            ->addHeading(1, 'Article Title', ['class' => 'display-4'])
            ->addParagraph('Published on ' . date('F j, Y'), ['class' => 'text-muted'])
            ->addClass('article-header'),
        ['class' => 'container mb-4']
    )
    ->addDiv(
        MFragment::factory()
            ->addDiv(
                MFragment::factory()
                    ->addParagraph('Article introduction...')
                    ->addImage('/media/hero-image.jpg', 'Hero Image', ['class' => 'img-fluid rounded'])
                    ->addParagraph('Main article content...'),
                ['class' => 'col-lg-8']
            )
            ->addDiv(
                MFragment::factory()
                    ->addHeading(3, 'Related Articles')
                    ->addList(['Article 1', 'Article 2', 'Article 3'], 'ul', ['class' => 'list-unstyled'])
                    ->addButton('Subscribe', 'button', ['class' => 'btn btn-primary']),
                ['class' => 'col-lg-4']
            ),
        ['class' => 'container']
    );

echo $article->show();

Dynamic Navigation Menu

// Create navigation from database data
$navigation = MFragment::factory()->addClass('navbar-nav');

foreach ($menuItems as $item) {
    $link = MFragment::factory()
        ->addLink($item['title'], $item['url'], [
            'class' => 'nav-link' . ($item['active'] ? ' active' : ''),
            'aria-current' => $item['active'] ? 'page' : null
        ]);
    
    $navigation->addDiv($link, ['class' => 'nav-item']);
}

echo $navigation->show();

Form with Validation Display

// Complex form structure with dynamic error handling
$form = MFragment::factory()
    ->addTagElement('form', 
        MFragment::factory()
            ->addDiv(
                MFragment::factory()
                    ->addTagElement('label', 'Email Address', ['for' => 'email', 'class' => 'form-label'])
                    ->addTagElement('input', null, [
                        'type' => 'email',
                        'class' => 'form-control' . ($hasEmailError ? ' is-invalid' : ''),
                        'id' => 'email',
                        'name' => 'email'
                    ])
                    ->addDiv($emailError ?? '', ['class' => 'invalid-feedback']),
                ['class' => 'mb-3']
            )
            ->addDiv(
                MFragment::factory()
                    ->addButton('Submit', 'submit', ['class' => 'btn btn-primary'])
                    ->addButton('Cancel', 'button', ['class' => 'btn btn-secondary ms-2']),
                ['class' => 'd-flex justify-content-end']
            ),
        ['method' => 'post', 'action' => '/submit']
    );

echo $form->show();

Data-Driven Component Lists

// Generate structures based on data
$productGrid = MFragment::factory()->addClass('row g-4');

foreach ($products as $product) {
    $card = MFragment::factory()
        ->addDiv(
            MFragment::factory()
                ->addDiv(
                    MFragment::factory()
                        ->addImage($product['image'], $product['title'], ['class' => 'card-img-top'])
                        ->addDiv(
                            MFragment::factory()
                                ->addHeading(5, $product['title'], ['class' => 'card-title'])
                                ->addParagraph($product['description'], ['class' => 'card-text'])
                                ->addDiv(
                                    MFragment::factory()
                                        ->addSpan($product['price'], ['class' => 'h5 text-primary'])
                                        ->addButton('Add to Cart', 'button', [
                                            'class' => 'btn btn-outline-primary btn-sm ms-2',
                                            'data-product-id' => $product['id']
                                        ]),
                                    ['class' => 'd-flex justify-content-between align-items-center']
                                ),
                            ['class' => 'card-body']
                        ),
                    ['class' => 'card h-100']
                ),
            ['class' => 'col-md-6 col-lg-4']
        );
    
    $productGrid->addComponent($card);
}

echo $productGrid->show();

API Reference

All components inherit these methods:

Base Methods for All Components

All components inherit these methods:

// Attribute Management
->setAttribute(string $name, mixed $value)
->addClass(string $class)
->setId(string $id)
->setData(string $key, mixed $value)

// Content Management  
->setContent(string $content)
->appendContent(string $content)
->prependContent(string $content)

// Rendering
->show(): string
->__toString(): string

Responsive Image Helpers

// Generate srcset for responsive images
generateSrcset(string $mediaFile, string $baseType): string

// Generate sizes attribute
generateSizesForType(string $baseType): string

// Generate complete picture element
generateResponsivePicture(string $mediaFile, array $options): string

Advanced Configuration

Creating and Placing Custom Components

MFragment offers several ways to organize custom components:

1. Project Components (Recommended)

Directory: src/components/
Namespace: Free choice (e.g., App\Components\, MyProject\Components\)

src/components/
├── Cards/
│   ├── ProductCard.php      -> App\Components\Cards\ProductCard
│   └── NewsCard.php         -> MyProject\Components\Cards\NewsCard
├── Navigation/
│   ├── MainMenu.php         -> App\Components\Navigation\MainMenu
│   └── Breadcrumb.php       -> YourNamespace\Components\Navigation\Breadcrumb
└── Layout/
    ├── Hero.php             -> App\Components\Layout\Hero
    └── Footer.php           -> CustomNamespace\Components\Layout\Footer

2. Theme Components (with Theme Addon)

Directory: src/addons/theme/components/ or src/addons/theme/private/components/
Namespace: Free choice (e.g., Theme\Components\, MyTheme\Components\)

src/addons/theme/components/
├── Sections/
│   └── HeroSection.php      -> Theme\Components\Sections\HeroSection
└── Widgets/
    └── ContactWidget.php    -> MyTheme\Components\Widgets\ContactWidget

3. Addon Components (for Addon Developers)

Directory: src/addons/{addon_name}/components/
Namespace: Free choice according to your addon namespace

src/addons/myproject/components/
├── Custom/
│   └── SpecialComponent.php -> MyProject\Components\Custom\SpecialComponent

Creating Components - Step by Step

1. Create File

<?php
// File: src/components/Cards/ProductCard.php
namespace YourNamespace\Components\Cards;

use FriendsOfRedaxo\MFragment\Components\AbstractComponent;

class ProductCard extends AbstractComponent
{
    private string $title = '';
    private string $price = '';
    
    public function setTitle(string $title): self
    {
        $this->title = $title;
        return $this;
    }
    
    public function setPrice(string $price): self
    {
        $this->price = $price;
        return $this;
    }
    
    protected function renderHtml(): string
    {
        return '<div' . $this->buildAttributesString() . '>
            <h3>' . htmlspecialchars($this->title) . '</h3>
            <span class="price">' . htmlspecialchars($this->price) . '</span>
        </div>';
    }
}

2. Use Component

<?php
use YourNamespace\Components\Cards\ProductCard;

// Direct usage
$card = ProductCard::factory()
    ->setTitle('Gaming Laptop')
    ->setPrice('€ 1,299.00')
    ->addClass('product-card');

echo $card->show();

// In MFragment Container
$container = MFragment::factory()
    ->addComponent($card)
    ->addClass('product-grid');

Automatic Loading

MFragment automatically loads components when properly placed:

// These directories are automatically scanned:
src/components/                         -> Your chosen namespace
theme_addon_path/components/            -> Your theme namespace
theme_addon_path/private/components/    -> Your theme namespace
src/addons/mfragment/components/        -> FriendsOfRedaxo\MFragment\Components\*

Important:

  • The namespace is freely selectable
  • Namespace structure must match directory structure
  • Composer autoload or corresponding configuration required

Debugging

Debug Mode

Enable debug output for development:

// In development environment
\FriendsOfRedaxo\MFragment\Core\RenderEngine::enableDebug();

// Components output debug information
$card = Card::factory()->setHeader('Debug Card')->show();
// Output: <!-- MFragment Debug: Card rendered in 0.5ms -->

Performance Optimization

// Enable performance monitoring in development
if (rex::isDebugMode()) {
    \FriendsOfRedaxo\MFragment\Core\RenderEngine::enableDebug();
}

// Retrieve performance statistics
$stats = \FriendsOfRedaxo\MFragment\Core\RenderEngine::getStats();
echo "Render calls: " . $stats['renderCalls'];
echo "Total time: " . $stats['processingTime'] . "ms";

Performance Monitoring

// Retrieve detailed performance statistics
$engine = \FriendsOfRedaxo\MFragment\Core\RenderEngine::getInstance();
$stats = $engine->getDetailedStats();

foreach ($stats['components'] as $component => $data) {
    echo "{$component}: {$data['count']} renders, {$data['time']}ms total\n";
}

Media Manager Tests

// Test responsive image generation
$srcset = generateSrcset('test.jpg', 'hero_16x9');
assertNotEmpty($srcset);
assertStringContains('hero_16x9_768', $srcset);

Optional Dependencies

  • FOR Html - Extended HTML generation (automatically detected)
  • Media Manager - For responsive image functionality

License

This project is licensed under the MIT License - see the LICENSE file for details.

Support

Credits

MFragment is developed and maintained by Friends Of REDAXO.

About

Fragment Sammlung

Resources

Code of conduct

Stars

18 stars

Watchers

4 watching

Forks

Releases

Packages

Used by

Contributors

Languages