Skip to content

FriendsOfREDAXO/mfragment

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

49 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

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

License

Code of conduct

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages