Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
c26dc2d
Move FieldLayout class
riasvdv Feb 9, 2026
9571ee8
Move FieldLayoutForm class
riasvdv Feb 9, 2026
69d5fb0
Update usage
riasvdv Feb 9, 2026
555b2ba
Move FieldLayoutFormTab
riasvdv Feb 9, 2026
c0e897d
Update usages
riasvdv Feb 9, 2026
d2bb57b
Move FieldLayoutTab
riasvdv Feb 9, 2026
ad59d35
Update usages
riasvdv Feb 9, 2026
b47f652
Move layout elements
riasvdv Feb 9, 2026
988310d
Update usages
riasvdv Feb 9, 2026
898e5f8
Add class_alias for all layout elements
riasvdv Feb 9, 2026
3bfd2b8
FieldLayoutProviderInterface
riasvdv Feb 9, 2026
f38734e
Pint & Rector
riasvdv Feb 9, 2026
807bf98
Woops
riasvdv Feb 9, 2026
e176884
Standalone FieldLayout class
riasvdv Feb 9, 2026
6b9eff3
FieldLayout, FieldLayoutComponent
riasvdv Feb 9, 2026
e077180
Move FieldLayoutElement
riasvdv Feb 9, 2026
d2565d7
FieldLayoutElement
riasvdv Feb 9, 2026
c133e92
Refactor, cleanup, fixes
riasvdv Feb 9, 2026
1f457fc
More refactoring
riasvdv Feb 9, 2026
07d7a71
wip
riasvdv Feb 9, 2026
1d9acc9
Replace Validator property with MessageBag to fix serialization
riasvdv Feb 9, 2026
4cf7fd8
Fix legacy test
riasvdv Feb 9, 2026
10f4158
Merge branch 'feature/validator-refactor' into feature/field-layouts
riasvdv Feb 10, 2026
252c42e
Remove columnSuffix
riasvdv Feb 10, 2026
dcb16e8
Use custom exceptions
riasvdv Feb 10, 2026
dd66610
Remove showCardsInGrid
riasvdv Feb 10, 2026
5533eb0
phpstan
riasvdv Feb 10, 2026
59c6705
More cleanup
riasvdv Feb 10, 2026
0e912cf
Cleanup applicationtrait
riasvdv Feb 10, 2026
75f7caf
Replace Yii's InvalidArgumentException with PHP default one
riasvdv Feb 10, 2026
b282dfc
phpstan?
riasvdv Feb 10, 2026
d8e09ac
Filehelper tests
riasvdv Feb 10, 2026
33b51b2
Add explicit requirement on yiisoft/arrays
riasvdv Feb 10, 2026
d75f01b
Changelog
riasvdv Feb 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions CHANGELOG-WIP.md
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,65 @@ Craft 6 introduces a new validation system that uses Laravel's Validator instead
- Removed `craft\console\controllers\EntryTypesController` in favor of:
- `CraftCms\Cms\Entry\Commands\MergeEntryTypesCommand`

## Component

- Added `CraftCms\Cms\Component\Component` base class, replacing Yii2's `BaseObject`/`Component` with config hydration, magic getters/setters, and `Arrayable` support.
- Added `CraftCms\Cms\Component\Exceptions\InvalidCallException`, replacing `yii\base\InvalidCallException`.
- Added `CraftCms\Cms\Component\Exceptions\UnknownPropertyException`, replacing `yii\base\UnknownPropertyException`.

## Field Layouts

### Added

- Added `CraftCms\Cms\FieldLayout\FieldLayoutForm`.
- Added `CraftCms\Cms\FieldLayout\FieldLayoutFormTab`.
- Added `CraftCms\Cms\FieldLayout\FieldLayoutFormElement`.
- Added `CraftCms\Cms\FieldLayout\FieldLayoutServiceProvider`.
- Added `CraftCms\Cms\FieldLayout\Concerns\HasFieldLayout` trait.

### Deprecations
- Deprecated `craft\models\FieldLayout`. `CraftCms\Cms\FieldLayout\FieldLayout` should be used instead.
- Deprecated `craft\models\FieldLayoutTab`. `CraftCms\Cms\FieldLayout\FieldLayoutTab` should be used instead.
- Deprecated `craft\base\FieldLayoutComponent`. `CraftCms\Cms\FieldLayout\FieldLayoutComponent` should be used instead.
- Deprecated `craft\base\FieldLayoutElement`. `CraftCms\Cms\FieldLayout\FieldLayoutElement` should be used instead.
- Deprecated `craft\base\FieldLayoutProviderInterface`. `CraftCms\Cms\FieldLayout\Contracts\FieldLayoutProviderInterface` should be used instead.
- Deprecated `craft\records\FieldLayout`. `CraftCms\Cms\FieldLayout\Models\FieldLayout` should be used instead.
- Deprecated `craft\fieldlayoutelements\BaseField`. `CraftCms\Cms\FieldLayout\LayoutElements\BaseField` should be used instead.
- Deprecated `craft\fieldlayoutelements\BaseNativeField`. `CraftCms\Cms\FieldLayout\LayoutElements\BaseNativeField` should be used instead.
- Deprecated `craft\fieldlayoutelements\BaseUiElement`. `CraftCms\Cms\FieldLayout\LayoutElements\BaseUiElement` should be used instead.
- Deprecated `craft\fieldlayoutelements\CustomField`. `CraftCms\Cms\FieldLayout\LayoutElements\CustomField` should be used instead.
- Deprecated `craft\fieldlayoutelements\Heading`. `CraftCms\Cms\FieldLayout\LayoutElements\Heading` should be used instead.
- Deprecated `craft\fieldlayoutelements\HorizontalRule`. `CraftCms\Cms\FieldLayout\LayoutElements\HorizontalRule` should be used instead.
- Deprecated `craft\fieldlayoutelements\Html`. `CraftCms\Cms\FieldLayout\LayoutElements\Html` should be used instead.
- Deprecated `craft\fieldlayoutelements\LineBreak`. `CraftCms\Cms\FieldLayout\LayoutElements\LineBreak` should be used instead.
- Deprecated `craft\fieldlayoutelements\Markdown`. `CraftCms\Cms\FieldLayout\LayoutElements\Markdown` should be used instead.
- Deprecated `craft\fieldlayoutelements\Template`. `CraftCms\Cms\FieldLayout\LayoutElements\Template` should be used instead.
- Deprecated `craft\fieldlayoutelements\TextField`. `CraftCms\Cms\FieldLayout\LayoutElements\TextField` should be used instead.
- Deprecated `craft\fieldlayoutelements\TextareaField`. `CraftCms\Cms\FieldLayout\LayoutElements\TextareaField` should be used instead.
- Deprecated `craft\fieldlayoutelements\Tip`. `CraftCms\Cms\FieldLayout\LayoutElements\Tip` should be used instead.
- Deprecated `craft\fieldlayoutelements\TitleField`. `CraftCms\Cms\FieldLayout\LayoutElements\TitleField` should be used instead.
- Deprecated `craft\fieldlayoutelements\FullNameField`. `CraftCms\Cms\FieldLayout\LayoutElements\FullNameField` should be used instead.
- Deprecated `craft\fieldlayoutelements\addresses\AddressField`. `CraftCms\Cms\FieldLayout\LayoutElements\addresses\AddressField` should be used instead.
- Deprecated `craft\fieldlayoutelements\addresses\CountryCodeField`. `CraftCms\Cms\FieldLayout\LayoutElements\addresses\CountryCodeField` should be used instead.
- Deprecated `craft\fieldlayoutelements\addresses\LabelField`. `CraftCms\Cms\FieldLayout\LayoutElements\addresses\LabelField` should be used instead.
- Deprecated `craft\fieldlayoutelements\addresses\LatLongField`. `CraftCms\Cms\FieldLayout\LayoutElements\addresses\LatLongField` should be used instead.
- Deprecated `craft\fieldlayoutelements\addresses\OrganizationField`. `CraftCms\Cms\FieldLayout\LayoutElements\addresses\OrganizationField` should be used instead.
- Deprecated `craft\fieldlayoutelements\addresses\OrganizationTaxIdField`. `CraftCms\Cms\FieldLayout\LayoutElements\addresses\OrganizationTaxIdField` should be used instead.
- Deprecated `craft\fieldlayoutelements\assets\AssetTitleField`. `CraftCms\Cms\FieldLayout\LayoutElements\assets\AssetTitleField` should be used instead.
- Deprecated `craft\fieldlayoutelements\assets\AltField`. `CraftCms\Cms\FieldLayout\LayoutElements\assets\AltField` should be used instead.
- Deprecated `craft\fieldlayoutelements\entries\EntryTitleField`. `CraftCms\Cms\FieldLayout\LayoutElements\entries\EntryTitleField` should be used instead.
- Deprecated `craft\fieldlayoutelements\users\UsernameField`. `CraftCms\Cms\FieldLayout\LayoutElements\users\UsernameField` should be used instead.
- Deprecated `craft\fieldlayoutelements\users\FullNameField`. `CraftCms\Cms\FieldLayout\LayoutElements\users\FullNameField` should be used instead.
- Deprecated `craft\fieldlayoutelements\users\EmailField`. `CraftCms\Cms\FieldLayout\LayoutElements\users\EmailField` should be used instead.
- Deprecated `craft\fieldlayoutelements\users\AffiliatedSiteField`. `CraftCms\Cms\FieldLayout\LayoutElements\users\AffiliatedSiteField` should be used instead.
- Deprecated `craft\fieldlayoutelements\users\PhotoField`. `CraftCms\Cms\FieldLayout\LayoutElements\users\PhotoField` should be used instead.
- Deprecated `craft\events\CreateFieldLayoutFormEvent`. `CraftCms\Cms\FieldLayout\Events\CreateFieldLayoutForm` should be used instead.
- Deprecated `craft\events\DefineFieldLayoutCustomFieldsEvent`. `CraftCms\Cms\FieldLayout\Events\DefineCustomFields` should be used instead.
- Deprecated `craft\events\DefineFieldLayoutElementsEvent`. `CraftCms\Cms\FieldLayout\Events\DefineUIElements` should be used instead.
- Deprecated `craft\events\DefineFieldLayoutFieldsEvent`. `CraftCms\Cms\FieldLayout\Events\DefineNativeFields` should be used instead.
- Deprecated `craft\events\DefineShowFieldLayoutComponentInFormEvent`. `CraftCms\Cms\FieldLayout\Events\DefineShowInForm` should be used instead.
- Deprecated `craft\events\DefineFieldActionsEvent`. `CraftCms\Cms\FieldLayout\Events\DefineActionMenuItems` should be used instead.

## Fields

- Removed `craft\controllers\FieldsController` in favor of `CraftCms\Cms\Http\Controllers\FieldsController`.
Expand Down
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
"twig/twig": "~3.21.1",
"web-auth/webauthn-lib": "~4.9.0",
"webonyx/graphql-php": "~14.11.10",
"yiisoft/arrays": "^3.2",
"yiisoft/html": "^3.11",
"yiisoft/translator": "^3.2",
"yiisoft/translator-message-php": "^1.1"
Expand Down
2 changes: 1 addition & 1 deletion composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion database/Factories/Concerns/HasFieldFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@

namespace CraftCms\Cms\Database\Factories\Concerns;

use craft\fieldlayoutelements\CustomField;
use CraftCms\Cms\Database\Factories\ElementFactoryResult;
use CraftCms\Cms\Element\Element;
use CraftCms\Cms\Field\Models\Field;
use CraftCms\Cms\FieldLayout\LayoutElements\CustomField;
use CraftCms\Cms\FieldLayout\Models\FieldLayout;
use CraftCms\Cms\Support\Facades\EntryTypes;
use CraftCms\Cms\Support\Facades\Fields;
Expand Down
2 changes: 1 addition & 1 deletion database/Factories/FieldLayoutFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@

namespace CraftCms\Cms\Database\Factories;

use craft\fieldlayoutelements\CustomField;
use CraftCms\Cms\Entry\Elements\Entry;
use CraftCms\Cms\Field\Field;
use CraftCms\Cms\Field\Models\Field as FieldModel;
use CraftCms\Cms\FieldLayout\LayoutElements\CustomField;
use CraftCms\Cms\FieldLayout\Models\FieldLayout;
use CraftCms\Cms\Support\Str;
use Illuminate\Database\Eloquent\Factories\Factory;
Expand Down
2 changes: 1 addition & 1 deletion resources/templates/_includes/forms/cardViewDesigner.twig
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
{% set fieldLayout = fieldLayout ?? create('craft\\models\\FieldLayout') %}
{% set fieldLayout = fieldLayout ?? create('CraftCms\\Cms\\FieldLayout\\FieldLayout') %}
{{ cardViewDesigner(fieldLayout, _context)|raw }}
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
{% set fieldLayout = fieldLayout ?? create('craft\\models\\FieldLayout') %}
{% set fieldLayout = fieldLayout ?? create('CraftCms\\Cms\\FieldLayout\\FieldLayout') %}
{{ fieldLayoutDesigner(fieldLayout, _context)|raw }}
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
{% set fieldLayout = fieldLayout ?? create('craft\\models\\FieldLayout') %}
{% set fieldLayout = fieldLayout ?? create('CraftCms\\Cms\\FieldLayout\\FieldLayout') %}
{{ generatedFieldsTable(fieldLayout, _context)|raw }}
6 changes: 3 additions & 3 deletions src/Address/Addresses.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@
use CommerceGuys\Addressing\Formatter\DefaultFormatter;
use CommerceGuys\Addressing\Formatter\FormatterInterface;
use Craft;
use craft\base\FieldLayoutProviderInterface;
use craft\models\FieldLayout;
use craft\models\FieldLayoutTab;
use CraftCms\Cms\Address\Elements\Address;
use CraftCms\Cms\Address\Events\DefineAddressCountries;
use CraftCms\Cms\Address\Events\DefineAddressFieldLabel;
Expand All @@ -25,6 +22,9 @@
use CraftCms\Cms\Address\Events\DefineAddressUsedSubdivisionFields;
use CraftCms\Cms\Address\Repositories\SubdivisionRepository;
use CraftCms\Cms\Field\Fields;
use CraftCms\Cms\FieldLayout\Contracts\FieldLayoutProviderInterface;
use CraftCms\Cms\FieldLayout\FieldLayout;
use CraftCms\Cms\FieldLayout\FieldLayoutTab;
use CraftCms\Cms\ProjectConfig\Events\ConfigEvent;
use CraftCms\Cms\ProjectConfig\ProjectConfig;
use CraftCms\Cms\ProjectConfig\ProjectConfigHelper;
Expand Down
2 changes: 1 addition & 1 deletion src/Address/Elements/Address.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
use craft\elements\actions\Copy;
use craft\elements\conditions\addresses\AddressCondition;
use craft\elements\conditions\ElementConditionInterface;
use craft\models\FieldLayout;
use craft\web\twig\AllowedInSandbox;
use CraftCms\Cms\Address\Addresses;
use CraftCms\Cms\Address\Models\Address as AddressModel;
Expand All @@ -23,6 +22,7 @@
use CraftCms\Cms\Database\Table;
use CraftCms\Cms\Element\Element;
use CraftCms\Cms\Element\Queries\AddressQuery;
use CraftCms\Cms\FieldLayout\FieldLayout;
use CraftCms\Cms\Shared\Concerns\HasNames;
use CraftCms\Cms\User\Elements\User;
use CraftCms\Cms\Validation\Attributes\Ruleset;
Expand Down
10 changes: 5 additions & 5 deletions src/Address/Validation/AddressRules.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@

namespace CraftCms\Cms\Address\Validation;

use craft\fieldlayoutelements\addresses\LatLongField;
use craft\fieldlayoutelements\addresses\OrganizationField;
use craft\fieldlayoutelements\addresses\OrganizationTaxIdField;
use craft\fieldlayoutelements\BaseNativeField;
use craft\fieldlayoutelements\FullNameField;
use CraftCms\Cms\Address\Addresses;
use CraftCms\Cms\Address\Elements\Address;
use CraftCms\Cms\Cms;
use CraftCms\Cms\Element\Element;
use CraftCms\Cms\Element\Validation\ElementRules;
use CraftCms\Cms\FieldLayout\LayoutElements\addresses\LatLongField;
use CraftCms\Cms\FieldLayout\LayoutElements\addresses\OrganizationField;
use CraftCms\Cms\FieldLayout\LayoutElements\addresses\OrganizationTaxIdField;
use CraftCms\Cms\FieldLayout\LayoutElements\BaseNativeField;
use CraftCms\Cms\FieldLayout\LayoutElements\FullNameField;
use CraftCms\Cms\Support\Arr;
use CraftCms\Cms\Validation\Rules\DisallowMb4;
use Illuminate\Validation\Rule;
Expand Down
7 changes: 4 additions & 3 deletions src/Asset/Elements/Asset.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@
use craft\helpers\ImageTransforms;
use craft\helpers\Template;
use craft\helpers\UrlHelper;
use craft\models\FieldLayout;
use craft\models\ImageTransform;
use craft\models\Volume;
use craft\models\VolumeFolder;
Expand All @@ -68,6 +67,7 @@
use CraftCms\Cms\Element\Queries\AssetQuery;
use CraftCms\Cms\Element\Queries\Contracts\ElementQueryInterface;
use CraftCms\Cms\Field\Field;
use CraftCms\Cms\FieldLayout\FieldLayout;
use CraftCms\Cms\Support\Arr;
use CraftCms\Cms\Support\Facades\I18N;
use CraftCms\Cms\Support\Facades\Users;
Expand All @@ -84,11 +84,11 @@
use Illuminate\Support\Facades\DB as DbFacade;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Log;
use InvalidArgumentException;
use Override;
use Stringable;
use Twig\Markup;
use yii\base\Exception;
use yii\base\InvalidArgumentException;
use yii\base\InvalidCallException;
use yii\base\InvalidConfigException;
use yii\base\NotSupportedException;
Expand Down Expand Up @@ -1288,7 +1288,8 @@ public function __get($name)

try {
return parent::__get($name);
} catch (UnknownPropertyException $e) {
/** @phpstan-ignore catch.neverThrown */
} catch (UnknownPropertyException|\CraftCms\Cms\Component\Exceptions\UnknownPropertyException $e) {
// Is $name a transform handle?
if (($transform = Craft::$app->getImageTransforms()->getTransformByHandle($name)) !== null) {
return $this->copyWithTransform($transform);
Expand Down
2 changes: 1 addition & 1 deletion src/Asset/Validation/AssetRules.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@

namespace CraftCms\Cms\Asset\Validation;

use craft\fieldlayoutelements\assets\AltField;
use CraftCms\Cms\Asset\Elements\Asset;
use CraftCms\Cms\Asset\Validation\Rules\AssetLocationRule;
use CraftCms\Cms\Element\Validation\ElementRules;
use CraftCms\Cms\FieldLayout\LayoutElements\assets\AltField;
use CraftCms\Cms\Validation\Rules\DisallowMb4;
use Illuminate\Validation\Rule;
use Override;
Expand Down
97 changes: 97 additions & 0 deletions src/Component/Component.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?php

declare(strict_types=1);

namespace CraftCms\Cms\Component;

use CraftCms\Cms\Component\Exceptions\InvalidCallException;
use CraftCms\Cms\Component\Exceptions\UnknownPropertyException;
use CraftCms\Cms\Support\Typecast;
use Illuminate\Contracts\Support\Arrayable;
use Yiisoft\Arrays\ArrayableInterface;
use Yiisoft\Arrays\ArrayableTrait;

abstract class Component implements Arrayable, ArrayableInterface
{
use ArrayableTrait;

public function __construct(array $config = [])
{
Typecast::properties(static::class, $config);

self::configure($this, $config);
}

/**
* Configures a component with the initial property values.
*
* @param self $component the component to be configured
* @param array $properties the property initial values given in terms of name-value pairs.
* @return self the component itself
*/
final public static function configure(self $component, array $properties = []): self
{
foreach ($properties as $name => $value) {
$component->$name = $value;
}

return $component;
}

public function __get(string $name)
{
$getter = 'get'.$name;

if (method_exists($this, $getter)) {
return $this->$getter();
}

if (method_exists($this, 'set'.$name)) {
throw new InvalidCallException('Getting write-only property: '.static::class.'::'.$name);
}

throw new UnknownPropertyException('Getting unknown property: '.static::class.'::'.$name);
}

public function __set(string $name, $value): void
{
$setter = 'set'.$name;

if (method_exists($this, $setter)) {
// set property
$this->$setter($value);

return;
}

if (method_exists($this, 'get'.$name)) {
throw new InvalidCallException('Setting read-only property: '.static::class.'::'.$name);
}

throw new UnknownPropertyException('Setting unknown property: '.static::class.'::'.$name);
}

public function __isset(string $name): bool
{
$getter = 'get'.$name;

if (method_exists($this, $getter)) {
return $this->$getter() !== null;
}

return false;
}

public function __unset(string $name): void
{
$setter = 'set'.$name;

if (method_exists($this, $setter)) {
$this->$setter(null);

return;
}

throw new InvalidCallException('Unsetting an unknown or read-only property: '.static::class.'::'.$name);
}
}
9 changes: 9 additions & 0 deletions src/Component/Exceptions/InvalidCallException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

namespace CraftCms\Cms\Component\Exceptions;

use Exception;

final class InvalidCallException extends Exception {}
9 changes: 9 additions & 0 deletions src/Component/Exceptions/UnknownPropertyException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

namespace CraftCms\Cms\Component\Exceptions;

use Exception;

final class UnknownPropertyException extends Exception {}
2 changes: 1 addition & 1 deletion src/Config/GeneralConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -6219,7 +6219,7 @@ public function rememberedUserSessionDuration(mixed $value): self
// Store the DateInterval separately for getRememberedUserSessionDuration()
try {
$interval = DateTimeHelper::toDateInterval($value);
} catch (\yii\base\InvalidArgumentException $e) {
} catch (InvalidArgumentException $e) {
throw new InvalidConfigException($e->getMessage(), 0, $e);
}

Expand Down
Loading
Loading