PHP code example of survos / field-bundle

1. Go to this page and download the library: Download survos/field-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/ */

    

survos / field-bundle example snippets


use Survos\FieldBundle\Attribute\Field;
use Survos\FieldBundle\Enum\Widget;

class Tenant
{
    #[Field(searchable: true, sortable: true, order: 10)]
    public string $name = '';

    #[Field(filterable: true, widget: Widget::Select, facet: true, order: 20)]
    public string $status = '';

    #[Field(sortable: true, format: 'date', order: 30)]
    public \DateTimeImmutable $createdAt;
}

#[With(description: 'Execution status', enum: ['pending', 'done', 'failed'])]  // LLM schema
#[ApiProperty('Current execution status')]                                       // OpenAPI
#[Field(filterable: true, widget: Widget::Select, facet: true)]                 // display/search
public AiTaskStatus $status;

use Survos\FieldBundle\Attribute\Field;
use Survos\FieldBundle\Enum\Widget;

#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD)]
class Field
{
    public function __construct(
        ?string $transKey   = null,   // translation key override (looked up in 'fields' domain)
        bool    $searchable = false,  // ible    = true,   // show by default (false = hidden but toggleable)
        int     $order      = 100,    // column display position (lower = further left)
        ?string $width      = null,   // CSS width hint, e.g. '8rem'
        ?string $format     = null,   // display format: 'date', 'datetime', 'currency', etc.
    ) {}
}

use Survos\FieldBundle\Attribute\EntityMeta;

#[EntityMeta(
    icon: 'mdi:building',
    group: 'Content',
    order: 10,
    label: 'Tenant',
    description: 'A workspace that can own projects and members.',
    adminBrowsable: true,
)]
class Tenant { ... }

use Survos\FieldBundle\Attribute\RouteIdentity;

// Simple: single field
#[RouteIdentity(field: 'code')]
class Tenant implements RouteParametersInterface
{
    use RouteIdentityTrait;
    #[ORM\Column] public string $code;
}

// Nested: child entity walks the parent chain automatically
#[RouteIdentity(field: 'code', parents: ['tenant'], key: 'projectCode')]
class Project implements RouteParametersInterface
{
    use RouteIdentityTrait;
    #[ORM\ManyToOne] public Tenant $tenant;
    #[ORM\Column]    public string $code;
}

// $project->getRp() → ['tenantId' => 'acme', 'projectCode' => 'photo-archive']
// No manual merge — the parent chain resolves automatically.

use Survos\CoreBundle\Entity\RouteParametersInterface;
use Survos\FieldBundle\Attribute\RouteIdentity;
use Survos\FieldBundle\Entity\RouteIdentityTrait;

#[RouteIdentity(field: 'id')]
class Image implements RouteParametersInterface
{
    use RouteIdentityTrait;

    #[ORM\Id]
    #[ORM\Column(length: 26)]
    public string $id;
}

#[Route('/image/{imageId}')]
final class ImageController extends AbstractController
{
    #[Route('/show', name: 'image_show')]
    public function show(Image $image): array
    {
        return ['image' => $image];
    }
}

#[RouteIdentity(field: 'code', key: 'intakeCode', parents: ['tenant'])]
class Intake implements RouteParametersInterface
{
    use RouteIdentityTrait;
}

#[Route('/{tenantId}/i/{intakeCode}')]
final class IntakeController extends AbstractController
{
    #[Route('/show', name: 'intake_show')]
    public function show(Intake $intake): array
    {
        return ['intake' => $intake];
    }
}

// Before (core-bundle)
class Owner implements RouteParametersInterface
{
    use RouteParametersTrait;
    public const array UNIQUE_PARAMETERS = ['ownerId' => 'code'];
}

// After (field-bundle)
#[RouteIdentity(field: 'code')]
class Owner implements RouteParametersInterface
{
    use RouteIdentityTrait;
}

use Survos\FieldBundle\Attribute\RouteMeta;
use Survos\FieldBundle\Enum\Audience;
use Survos\FieldBundle\Enum\Purpose;

#[Route('/tenant/{tenantId}', name: 'tenant_show')]
#[RouteMeta(
    description: 'Public overview page for a tenant.',
    entity: Tenant::class,
    purpose: Purpose::Show,
    audience: Audience::Public,
    sitemap: true,
    changefreq: 'weekly',
)]
public function show(Tenant $tenant): array { ... }

use Survos\FieldBundle\Attribute\ControllerMeta;

#[Route('/tenant/{tenantId}')]
#[ControllerMeta(entity: Tenant::class, audience: Audience::Authenticated)]
final class TenantController extends AbstractController
{
    #[Route('', name: 'tenant_show')]
    #[RouteMeta(description: 'Tenant detail page', purpose: Purpose::Show, audience: Audience::Public)]
    public function show(Tenant $tenant): array { ... }

    // Inherits entity: Tenant::class, audience: Audience::Authenticated from ControllerMeta
    #[Route('/edit', name: 'tenant_edit')]
    #[RouteMeta(description: 'Edit tenant settings', purpose: Purpose::Edit)]
    public function edit(Tenant $tenant, Request $request): array|RedirectResponse { ... }
}

use Survos\FieldBundle\Service\FieldReader;
use Survos\FieldBundle\Model\FieldDescriptor;

class MyService
{
    public function __construct(private readonly FieldReader $fieldReader) {}

    public function buildSearchConfig(string $class): array
    {
        $descriptors = $this->fieldReader->getDescriptors($class);

        return [
            'searchable' => array_map(fn (FieldDescriptor $d) => $d->name,
                array_filter($descriptors, fn ($d) => $d->searchable)),
            'sortable'   => array_map(fn (FieldDescriptor $d) => $d->name,
                array_filter($descriptors, fn ($d) => $d->sortable)),
        ];
    }

    // Get a single property descriptor
    public function getLabel(string $class, string $property): string
    {
        $d = $this->fieldReader->getDescriptor($class, $property);
        return $d?->getFallbackLabel() ?? $property;
    }
}

$d->getTranslationKey()     // transKey ?? name
$d->getTranslationDomain()  // 'fields' (always)
$d->getFallbackLabel()      // TitleCase of name, e.g. 'accountType' → 'Account Type'
$d->resolvedWidget()        // widget ?? inferred from type (null when not filterable)
$d->inputType()             // HTML input type: 'email'|'url'|'number'|'datetime-local'|'text'