PHP code example of c975l / config-bundle

1. Go to this page and download the library: Download c975l/config-bundle library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

c975l / config-bundle example snippets


// src/Entity/User.php
use c975L\ConfigBundle\Contract\UserInterface;

class User implements UserInterface
{
    // ...
}

use c975L\ConfigBundle\Service\Export\ExportFormat;
use c975L\ConfigBundle\Service\Export\TableExporter;
use Doctrine\DBAL\Connection;
use EasyCorp\Bundle\EasyAdminBundle\Attribute\AdminRoute;
use EasyCorp\Bundle\EasyAdminBundle\Config\Action;
use EasyCorp\Bundle\EasyAdminBundle\Config\ActionGroup;
use EasyCorp\Bundle\EasyAdminBundle\Config\Actions;
use EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext;
use Symfony\Component\HttpFoundation\Response;

class MyEntityCrudController extends AbstractCrudController
{
    public function __construct(
        private readonly Connection $connection,
        private readonly TableExporter $tableExporter,
    ) {}

    public function configureActions(Actions $actions): Actions
    {
        $exportGroup = ActionGroup::new('export', 'Export', 'fa fa-download')
            ->createAsGlobalActionGroup()
            ->addAction(Action::new('exportSql', 'SQL')->linkToCrudAction('exportSql'))
            ->addAction(Action::new('exportCsv', 'CSV')->linkToCrudAction('exportCsv'))
            ->addAction(Action::new('exportJson', 'JSON')->linkToCrudAction('exportJson'))
        ;

        return $actions->add(Crud::PAGE_INDEX, $exportGroup);
    }

    #[AdminRoute]
    public function exportSql(AdminContext $context): Response
    {
        // Set 'primary_key' to enable ON DUPLICATE KEY UPDATE; omit it for a plain INSERT-only dump
        return $this->tableExporter->export(ExportFormat::Sql, 'my_table', $this->fetchRows());
    }

    #[AdminRoute]
    public function exportCsv(AdminContext $context): Response
    {
        return $this->tableExporter->export(ExportFormat::Csv, 'my_table', $this->fetchRows());
    }

    #[AdminRoute]
    public function exportJson(AdminContext $context): Response
    {
        return $this->tableExporter->export(ExportFormat::Json, 'my_table', $this->fetchRows());
    }

    private function fetchRows(): array
    {
        return $this->connection->fetchAllAssociative('SELECT * FROM `my_table`');
    }
}

namespace c975L\MyBundle\Management;

use c975L\ConfigBundle\Management\ImportProviderInterface;

class MyImportProvider implements ImportProviderInterface
{
    // $kind is the string embedded in the export payload (see ContentExporter::export()), stable across dev/prod (e.g. "site_page")
    public function supportsImport(string $kind): bool
    {
        return 'my_entity' === $kind;
    }

    // $items are the payload's raw "items" array, one entry per exported entity. $filesDir is the directory the export's zip was extracted into — any 'file' reference inside $items is relative to it, null for a kind that never carries files. Match by a natural key (slug/name...), never a raw id: dev and prod ids never need to match. Returns ['created' => int, 'updated' => int]
    public function import(array $items, ?string $filesDir = null): array
    {
        // ...
    }
}

namespace c975L\MyBundle\Management;

use c975L\ConfigBundle\Management\ExportProviderInterface;

class MyExportProvider implements ExportProviderInterface
{
    // The string embedded in the export payload for this provider's items (see ContentExporter), stable across dev/prod (e.g. "my_entity")
    public function getKind(): string
    {
        return 'my_entity';
    }

    // Same shapes ContentExporter::export() expects: 'items' (JSON-able array, one entry per exported entity) and 'files' (archive-relative path => disk path, empty for a kind that never carries files)
    public function exportAll(): array
    {
        return ['items' => $this->fetchItems(), 'files' => []];
    }
}

namespace c975L\MyBundle\Management;

use c975L\ConfigBundle\Management\MenuProviderInterface;
use c975L\MyBundle\Controller\Management\MyCrudController;

class MenuProvider implements MenuProviderInterface
{
    public function getMenuSection(): array
    {
        return [
            'label' => 'label.my_section',
            'translation_domain' => 'my_bundle',
        ];
    }

    public function getMenus(): array
    {
        return [
            'my_entity' => [
                'controller' => MyCrudController::class,
                'label' => 'label.my_entity',
                'translation_domain' => 'my_bundle',
                'icon' => 'fas fa-star',
            ],
        ];
    }

    // Links to plain routes (not EasyAdmin CRUD controllers); return [] if none
    public function getLinks(): array
    {
        return [];
    }
}

public function getLinks(): array
{
    return [
        'shop' => [
            'name' => 'shop_index',
            'label' => 'label.shop',
            'translation_domain' => 'shop',
            'icon' => 'fas fa-shop',
        ],
    ];
}

'showcase' => [
    'url' => 'https://example.com/showcase',
    'label' => 'label.showcase',
    'translation_domain' => 'my_bundle',
    'icon' => 'fas fa-shapes',
],

namespace c975L\MyBundle\Management;

use c975L\ConfigBundle\Management\LinkableRouteProviderInterface;

class LinkableRouteProvider implements LinkableRouteProviderInterface
{
    // Route name => ['label' => translation key, 'translation_domain' => domain]; return [] if none
    public function getLinkableRoutes(): array
    {
        return [
            'my_bundle_display' => [
                'label' => 'label.my_page',
                'translation_domain' => 'my_bundle',
            ],
        ];
    }
}

namespace c975L\MyBundle\Management;

use c975L\ConfigBundle\Management\ImportmapProviderInterface;

class ImportmapProvider implements ImportmapProviderInterface
{
    // Import name => ['path' => string, 'entrypoint' => bool]. 'path' is relative to the project root, exactly as it should appear in importmap.php
    public function getAdminImportmapEntries(): array
    {
        return [
            '@c975l/my-bundle/controllers-admin.js' => [
                'path' => './vendor/c975l/my-bundle/assets/controllers-admin.js',
                'entrypoint' => true,
            ],
        ];
    }

    public function getImportmapEntries(): array
    {
        return [];
    }
}

namespace c975L\MyBundle\Management;

use c975L\ConfigBundle\Management\SitemapProviderInterface;

class MySitemapProvider implements SitemapProviderInterface
{
    // Gives public/sitemap-my-bundle.xml - keep it short and stable, it ends up in a public url
    public function getSitemapName(): string
    {
        return 'my-bundle';
    }

    public function getUrls(): array
    {
        return [[
            'loc' => 'https://example.com/my-thing/some-slug',
            'lastmod' => '2026-07-26',
            'changefreq' => 'monthly',
            'priority' => 8,
        ]];
    }
}

namespace c975L\MyBundle\Management;

use c975L\ConfigBundle\Management\WhatsNewJsonReader;
use c975L\ConfigBundle\Management\WhatsNewProviderInterface;

class WhatsNewProvider implements WhatsNewProviderInterface
{
    public function getEntries(): array
    {
        return WhatsNewJsonReader::read(\dirname(__DIR__, 2) . '/config/whatsnew.json');
    }
}

namespace c975L\MyBundle\Management;

use c975L\ConfigBundle\Entity\Config;
use c975L\ConfigBundle\Management\AlertProviderInterface;

class MyAlertProvider implements AlertProviderInterface
{
    public function getAlerts(): array
    {
        return [
            [
                'label' => 'My entity label',
                'description' => 'Why it needs attention',
                'severity' => Config::SEVERITY_WARNING,
                'url' => '/management/my-entity/edit/1',
            ],
        ];
    }
}

namespace c975L\MyBundle\Management;

use c975L\ConfigBundle\Management\ShortcutProviderInterface;
use c975L\MyBundle\Controller\Management\MyShortcutController;
use Symfony\Contracts\Translation\TranslatorInterface;

class MyShortcutProvider implements ShortcutProviderInterface
{
    public function __construct(
        private readonly TranslatorInterface $translator,
    ) {
    }

    public function getShortcuts(): array
    {
        return [
            [
                'label' => $this->translator->trans('label.toggle_maintenance', [], 'my_bundle'),
                'icon' => 'fas fa-wrench',
                'route' => MyShortcutController::TOGGLE_MAINTENANCE_ROUTE,
                'active' => $this->isMaintenanceOn(),
                'role' => 'ROLE_SUPER_ADMIN',
                'category' => ShortcutProviderInterface::CATEGORY_MAINTENANCE,
            ],
        ];
    }
}

namespace c975L\MyBundle\Management;

use c975L\ConfigBundle\Management\EssentialActionProviderInterface;

class MyEssentialActionProvider implements EssentialActionProviderInterface
{
    public function getEssentialActions(): array
    {
        return [
            [
                'slug' => 'my-action',
                'label' => 'label.my_essential_action',
                'description' => 'description.my_essential_action',
                'translation_domain' => 'my_bundle',
                'url' => '/management/my-entity',
                'isDone' => $this->isConfigured(),
                'order' => 50,
            ],
        ];
    }
}

namespace c975L\MyBundle\Management;

use c975L\ConfigBundle\Management\GuidedProjectProviderInterface;

class MyGuidedProjectProvider implements GuidedProjectProviderInterface
{
    public function getGuidedProjects(): array
    {
        return [
            [
                'slug' => 'creer-page',
                'label' => 'label.guided_project_creer_page',
                'description' => 'description.guided_project_creer_page',
                'translation_domain' => 'my_bundle',
                'order' => 10,
                'steps' => [
                    ['label' => 'label.step_open_pages', 'url' => '/management/page'],
                    ['label' => 'label.step_click_new', 'description' => 'description.step_click_new', 'highlight' => '.action-new'],
                    ['label' => 'label.step_done'],
                ],
            ],
        ];
    }
}

namespace c975L\MyBundle\Management;

use c975L\ConfigBundle\Management\DashboardWidgetProviderInterface;

class MyDashboardWidgetProvider implements DashboardWidgetProviderInterface
{
    public function getDashboardWidgets(): array
    {
        if (!$this->isEnabled()) {
            return [];
        }

        return [
            ['template' => '@MyBundle/management/_my_widget.html.twig', 'context' => ['foo' => 'bar']],
        ];
    }
}

namespace c975L\MyBundle\Tests\Management;

use c975L\ConfigBundle\Test\ManagementTargetsTestCase;
use c975L\MyBundle\Management\MenuProvider;
use c975L\MyBundle\Management\MyGuidedProjectProvider;

class ManagementTargetsTest extends ManagementTargetsTestCase
{
    protected function managementProviders(): iterable
    {
        return [
            new MenuProvider($this->createStub(ConfigServiceInterface::class)),
            // A provider generating urls takes these two recorders, so the targets behind its urls can be read back
            new MyGuidedProjectProvider($this->adminUrlGenerator(), $this->urlGenerator()),
        ];
    }

    // ConfigBundle's own controllers are watched by default (every bundle links to its screens); add yours
    protected function controllerDirectories(): array
    {
        return [...parent::controllerDirectories(), __DIR__ . '/../../src/Controller'];
    }
}

->add($this->spreader->spread('# */6 * * *', new RunCommandMessage('c975l:config:backup')))
->add($this->spreader->spread('# #(2-5) * * 1', new RunCommandMessage('c975l:config:backup:digest')))

use c975L\ConfigBundle\Scheduler\ScheduleSpreader;

public function __construct(
    private readonly ScheduleSpreader $spreader,
    private readonly CacheInterface $cache,
) {
}

public function getSchedule(): Schedule
{
    return (new Schedule())
        ->stateful($this->cache)
        ->add($this->spreader->spread('# #(0-2) * * *', new RunCommandMessage('c975l:sitemaps:create')))
        ->add($this->spreader->spread('# */6 * * *', new RunCommandMessage('c975l:config:backup')))
    ;
}

namespace c975L\ShopBundle\Scheduler;

use c975L\ConfigBundle\Scheduler\MaintenanceTask;
use c975L\ConfigBundle\Scheduler\MaintenanceTaskProviderInterface;

class ShopMaintenanceTaskProvider implements MaintenanceTaskProviderInterface
{
    public function getMaintenanceTasks(): array
    {
        return [
            // Expired download links, nightly
            new MaintenanceTask('# #(1-3) * * *', 'c975l:shop:downloads:delete'),
            // Product affinities, monthly: a full pass over the orders, too long to run nightly for what it changes
            new MaintenanceTask('# #(2-5) # * *', 'c975l:shop:affinity:calculate'),
        ];
    }
}

// src/Scheduler/MaintenanceSchedule.php, scaffolded by c975l/site-bundle
return $this->builder->addTasks((new Schedule())->stateful($this->cache));

namespace c975L\MyBundle\Management;

use c975L\ConfigBundle\Entity\HealthCheckResult;
use c975L\ConfigBundle\Management\HealthCheckProviderInterface;

class MyHealthCheckProvider implements HealthCheckProviderInterface
{
    // Stable identifier for this provider's rows (eg. "my-check") - used for --kind= filtering and stored on every HealthCheckResult
    public function getKind(): string
    {
        return 'my-check';
    }

    // One entry per checked url: ['url', 'label', 'status' => HealthCheckResult::STATUS_*, 'summary', 'details' => array, 'editUrl']
    public function runChecks(): array
    {
        return [
            [
                'url' => 'https://example.com/pages/home/',
                'label' => 'Home',
                'status' => HealthCheckResult::STATUS_OK,
                'summary' => 'Everything checks out',
                'details' => null,
                'editUrl' => '/management/my-entity/1/edit',
            ],
        ];
    }
}

use c975L\ConfigBundle\Attribute\AsHealthCheck;

// A run holding thousands of urls has no business on the same schedule as a handful of pages
#[AsHealthCheck(frequency: AsHealthCheck::FREQUENCY_MONTHLY)]
class MyHeavyHealthCheckProvider implements HealthCheckProviderInterface

class MyGeneratedHealthCheckProvider implements HealthCheckProviderInterface, HealthCheckFrequencyAwareInterface
{
    public function __construct(private readonly string $frequency = AsHealthCheck::FREQUENCY_WEEKLY)
    {
    }

    public function getFrequency(): string
    {
        return $this->frequency;
    }
}

namespace c975L\MyBundle\Management;

use c975L\ConfigBundle\Entity\HealthCheckResult;
use c975L\ConfigBundle\Management\HealthCheckAdviceBuilder;
use c975L\ConfigBundle\Management\HealthCheckAdviceProviderInterface;

class MyHealthCheckAdviceProvider implements HealthCheckAdviceProviderInterface
{
    // Keyed per result, via HealthCheckAdviceBuilder::key() (only the results this provider actually has something to say about) - $results is the same HealthCheckResult[] the current screen renders (dashboard "Health check" page or a CRUD's own scoped tab)
    public function buildAdvice(array $results): array
    {
        $advice = [];

        foreach ($results as $result) {
            if ('my-check' !== $result->getKind()) {
                continue;
            }

            $advice[HealthCheckAdviceBuilder::key($result)] = [
                [
                    'text' => '3 images are missing an alt text',
                    'url' => '/management/my-entity/1/edit',
                    // Optional - the individual offenders behind that line, rendered as a collapsed list under it
                    'items' => [
                        ['text' => 'banner.jpg', 'url' => '/management/my-entity/1/edit#block-4', 'label' => 'Edit the block'],
                    ],
                ],
            ];
        }

        return $advice;
    }
}

namespace c975L\MyBundle\Management;

use c975L\ConfigBundle\Management\StatusProviderInterface;

class MyStatusProvider implements StatusProviderInterface
{
    // The key this provider occupies in the report's "extra" section
    public function getStatusKey(): string
    {
        return 'shop';
    }

    // Counts and dates, nothing else: it travels over the network, and a receiver has no way to know a key is confidential
    public function getStatusData(): array
    {
        return [
            'pendingOrders' => $this->orderRepository->countPending(),
            'lastOrderAt' => $this->orderRepository->findLastDate()?->format(\DateTimeInterface::ATOM),
        ];
    }
}

namespace App\Management;

use c975L\ConfigBundle\Management\DevProfilePathProviderInterface;
use Symfony\Component\DependencyInjection\Attribute\When;

#[When('dev')]
class MyDevProfilePathProvider implements DevProfilePathProviderInterface
{
    public function __construct(
        private readonly MyRepository $myRepository,
    ) {
    }

    // One entry per path to profile: ['path' => local absolute path, 'label' => ?string]
    public function getPaths(): array
    {
        $paths = [];
        foreach ($this->myRepository->findAllPublished() as $item) {
            $paths[] = ['path' => '/shop/' . $item->getSlug(), 'label' => $item->getName()];
        }

        return $paths;
    }
}

namespace c975L\MyBundle\Management;

use c975L\ConfigBundle\Management\ProcedureJsonReader;
use c975L\ConfigBundle\Management\ProcedureProviderInterface;

class MyProcedureProvider implements ProcedureProviderInterface
{
    public function getProcedures(): array
    {
        return ProcedureJsonReader::read(\dirname(__DIR__, 2) . '/config/procedures.json');
    }
}

use c975L\ConfigBundle\Service\ConfigServiceInterface;

class MyService
{
    public function __construct(
        private readonly ConfigServiceInterface $configService,
    ) {}

    public function doSomething(): void
    {
        $siteName  = $this->configService->get('site-name'); // string
        $maxItems  = $this->configService->get('max-items'); // int (auto-cast)
        $isEnabled = $this->configService->get('feature-enabled'); // bool (auto-cast)
        $env       = $this->configService->getContainerParameter('kernel.environment');
    }
}
bash
php bin/console doctrine:migrations:diff
php bin/console doctrine:migrations:migrate
bash
php bin/console c975l:scaffold:install --dry-run
php bin/console c975l:scaffold:install
php bin/console c975l:config:user-create
bash
php -r "echo bin2hex(random_bytes(32)), PHP_EOL;"
bash
php bin/console c975l:config:encrypt-sensitive
bash
php bin/console messenger:consume async scheduler_site