diff --git a/.editorconfig b/.editorconfig
index dca0534cd0..b2621ff980 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -1,3 +1,7 @@
+# DO NOT EDIT THIS FILE!
+#
+# It's auto-generated by sonata-project/dev-kit package.
+
root = true
[*]
diff --git a/.flintci.yml b/.flintci.yml
index 8258ef2e91..0de2252fa4 100644
--- a/.flintci.yml
+++ b/.flintci.yml
@@ -1,3 +1,7 @@
+# DO NOT EDIT THIS FILE!
+#
+# It's auto-generated by sonata-project/dev-kit package.
+
services:
composernormalize: true
phpcsfixer: true
diff --git a/.gitattributes b/.gitattributes
index 667b29d794..14d001e6cf 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,3 +1,7 @@
+# DO NOT EDIT THIS FILE!
+#
+# It's auto-generated by sonata-project/dev-kit package.
+
.* export-ignore
*.md export-ignore
tests export-ignore
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
deleted file mode 100644
index 208ccb2658..0000000000
--- a/.github/ISSUE_TEMPLATE.md
+++ /dev/null
@@ -1,52 +0,0 @@
-
-
-
-
-### Environment
-
-#### Sonata packages
-
-```
-$ composer show --latest 'sonata-project/*'
-# Put the result here.
-```
-
-#### Symfony packages
-
-```
-$ composer show --latest 'symfony/*'
-# Put the result here.
-```
-
-#### PHP version
-
-```
-$ php -v
-# Put the result here.
-```
-
-## Subject
-
-
-
-## Steps to reproduce
-
-## Expected results
-
-## Actual results
-
-
diff --git a/.github/ISSUE_TEMPLATE/Question.md b/.github/ISSUE_TEMPLATE/Question.md
deleted file mode 100644
index e363f8e5cb..0000000000
--- a/.github/ISSUE_TEMPLATE/Question.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-name: ⛔ NO support questions
-about: If you have a question, please check out our Slack or StackOverflow!
----
-
-Hi, we try to keep Github issues for bug reports and feature requests only.
-If you have a question, please ask it on Stack Overflow or on #sonata on [the symfony-devs slack](https://symfony.com/slack-invite).
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 0000000000..7876e7c920
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,12 @@
+# DO NOT EDIT THIS FILE!
+#
+# It's auto-generated by sonata-project/dev-kit package.
+
+blank_issues_enabled: false
+contact_links:
+ - name: StackOverflow
+ url: https://stackoverflow.com/questions/tagged/sonata
+ about: 'Questions tagged with "sonata" on StackOverflow'
+ - name: Slack
+ url: https://symfony-devs.slack.com/archives/C3GC7MKM5
+ about: '#sonata channel on Symfony Devs Slack'
diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml
index ee08883498..0e4647bc10 100644
--- a/.github/workflows/lint.yaml
+++ b/.github/workflows/lint.yaml
@@ -1,3 +1,7 @@
+# DO NOT EDIT THIS FILE!
+#
+# It's auto-generated by sonata-project/dev-kit package.
+
on: [push, pull_request]
name: Lint
diff --git a/.github/workflows/qa.yaml b/.github/workflows/qa.yaml
index a796313469..0a7eb5136a 100644
--- a/.github/workflows/qa.yaml
+++ b/.github/workflows/qa.yaml
@@ -12,3 +12,11 @@ jobs:
REQUIRE_DEV: true
with:
args: analyse
+ psalm:
+ name: Psalm
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v2
+ - name: Psalm
+ uses: docker://vimeo/psalm-github-actions
diff --git a/.github/workflows/stale.yaml b/.github/workflows/stale.yaml
index dd4acf6ea9..9679ec46c7 100644
--- a/.github/workflows/stale.yaml
+++ b/.github/workflows/stale.yaml
@@ -1,3 +1,7 @@
+# DO NOT EDIT THIS FILE!
+#
+# It's auto-generated by sonata-project/dev-kit package.
+
# https://github.com/actions/stale
name: "Stale"
diff --git a/.php_cs.dist b/.php_cs.dist
index 47f50fea69..6f7ff02b54 100644
--- a/.php_cs.dist
+++ b/.php_cs.dist
@@ -1,6 +1,6 @@
+
# Sonata project contribution
Thanks for your interest in Sonata projects!
@@ -266,15 +272,17 @@ The deprecated minor version **MUST NOT** be provided. Use `x` instead. It will
Any deprecation **MUST** be documented in the corresponding `UPGRADE-[0-9].x.md`.
The documentation **MUST** be filled inside the top **unreleased** section with a sub title.
-The `NEXT_MAJOR` tag SHOULD not be used for deprecation.
-The `@deprecated` and `E_USER_DEPRECATED` key will be searched for before releasing the next major version.
+The `NEXT_MAJOR` tag SHOULD also be used for deprecations, it will be searched for before releasing the next major version.
You have three ways to deprecate things.
-For class definitions, methods (or first level functions) and properties, use the `@deprecated` tag:
+For class definitions and properties, use the `@deprecated` tag.
+For methods, use the `@deprecated` tag and trigger a deprecation with `@trigger_error('...', E_USER_DEPRECATED)`:
```php
/**
+ * NEXT_MAJOR: Remove this class.
+ *
* @deprecated since sonata-project/foo-lib 42.x, to be removed in 43.0. Use Shiny\New\ClassOfTheMonth instead.
*/
final class IAmOldAndUseless
@@ -284,15 +292,24 @@ final class IAmOldAndUseless
final class StillUsedClass
{
/**
+ * NEXT_MAJOR: Remove this property.
+ *
* @deprecated since sonata-project/foo-lib 42.x, to be removed in 43.0.
*/
public $butNotThisProperty;
/**
+ * NEXT_MAJOR: Remove this method.
+ *
* @deprecated since sonata-project/foo-lib 42.x, to be removed in 43.0.
*/
public function iAmBatman()
{
+ @trigger_error(sprintf(
+ 'Method %s() is deprecated since sonata-project/foo-lib 42.x and will be removed in version 43.0.',
+ __METHOD__
+ ), E_USER_DEPRECATED);
+
echo "But this is not Gotham here.";
}
}
@@ -301,6 +318,7 @@ final class StillUsedClass
If the deprecated thing is a service, you **MUST** specify it on the service definition:
```xml
+
The "%service_id%" service is deprecated since sonata-project/bar-bundle 42.x and will be removed in 43.0.
@@ -309,14 +327,15 @@ If the deprecated thing is a service, you **MUST** specify it on the service def
More info: http://symfony.com/blog/new-in-symfony-2-8-deprecated-service-definitions
-For everything else, not managed by the `@deprecated` tag, you **MUST** trigger a deprecation message.
+For everything else, not managed by the `@deprecated` tag,
+you **MUST** still trigger a deprecation message (and add a `NEXT_MAJOR` comment).
```php
+
# Sonata Admin Bundle
The missing Symfony Admin Generator
diff --git a/UPGRADE-2.1.md b/UPGRADE-2.1.md
deleted file mode 100644
index 206005b6a4..0000000000
--- a/UPGRADE-2.1.md
+++ /dev/null
@@ -1,13 +0,0 @@
-UPGRADE FROM 2.0 to 2.1
-=======================
-
-### Form
-
- * Due to refactoring in the Form Component, some type definitions have been changed and
- new ones have been introduced:
-
- * sonata_type_model : this type now only renders a standard select widget or a list
- widget (if the `multiple` option is set)
-
- * sonata_type_model_list : this type replaces the option `edit = list` provided as
- a 4th argument on the `sonata_type_model`
diff --git a/UPGRADE-2.2.md b/UPGRADE-2.2.md
deleted file mode 100644
index 493442834b..0000000000
--- a/UPGRADE-2.2.md
+++ /dev/null
@@ -1,10 +0,0 @@
-UPGRADE FROM 2.1 to 2.2
-=======================
-
-### Form
-
- * Refactoring the general layout to be more compact
- * Remove the collapsing option to migrate to tabbed form layout
- * Add tab menu to edit form
- * Fix deprecated calls to the Form API
-
diff --git a/UPGRADE-2.3.md b/UPGRADE-2.3.md
deleted file mode 100644
index 8497d87828..0000000000
--- a/UPGRADE-2.3.md
+++ /dev/null
@@ -1,19 +0,0 @@
-UPGRADE FROM 2.2 to 2.3
-=======================
-
-### Dependencies
-
-You'll need to follow the dependencies upgrade instructions.
-
-### Templates
-
- - `standard_layout.html.twig` has been updated (refactored layout accordingly to the theme). Some blocks have been moved, and the sonata_side_nav twig block became the main menu.
- - Admin's sidemenus are now tab-menus; former tab-menus are now collapsible items.
-
-### Admin classes
-
- - `configureSideMenu` and `buildSideMenu` methods of the `Admin` class have been deprecated; they are replaced by `configureTabMenu` and `buildTabMenu`.
-
-### Remove configureShowField
-
- - `configureShowField` has been deprecated since a while now, use `configureShowFields` instead => add a `s`.
\ No newline at end of file
diff --git a/UPGRADE-3.0.md b/UPGRADE-3.0.md
deleted file mode 100644
index 696cf71765..0000000000
--- a/UPGRADE-3.0.md
+++ /dev/null
@@ -1,40 +0,0 @@
-UPGRADE FROM 2.x to 3.0
-=======================
-
-### Dependencies
-
-You will need to follow the dependencies upgrade instructions.
-
-## Deprecated BaseFieldDescription::camelize() and AdminHelper::camelize()
-
-If you were using this method, please use `Doctrine\Common\Inflector\Inflector::classify()` instead.
-
-## Datagrid builders
-
-If you have implemented a custom datagrid builder, you must adapt the signature of its `addFilter` method to match the one in `DatagridBuilderInterface` again.
-
-## sonata_type_model_autocomplete
-CSS class `sonata-autocomplete-dropdown-item` is not automatically added to dropdown autocomplete item in `sonata_type_model_autocomplete`, use option `dropdown_item_css_class` to set the CSS class of dropdown item.
-
-## Standard Layout
-`sonata_wrapper` block was moved and is now inside the `.wrapper` div of admin lte theme.
-
-## ErrorElement
-
-The inline validation has been migrating to CoreBundle. Just rename `Sonata\AdminBundle\Validator\ErrorElement` to `Sonata\CoreBundle\Validator\ErrorElement`
-
-## AdminLTE 2
-
-AdminLTE version 2 has been integrated, this should work out of the box if you havn't change templates. If not you can review the upgrade guide here : [http://almsaeedstudio.com/themes/AdminLTE/documentation/index.html#upgrade](http://almsaeedstudio.com/themes/AdminLTE/documentation/index.html#upgrade)
-
-## AclSecurityHandler
-
-In order to fix deprecated issue by spiting `SecurityContextInterface`, `AclSecurityHandler` constructor got a new argument.
-
-## AdminPoolLoader
-
-If you're using a custom implementation of `sonata.admin.route_loader` service, make sure to provide an array as 2nd argument since the type for this argument is now hinted to `array`.
-
-## LegacyModelsToArrayTransformer
-
-The `ModelsToArrayTransformer` has been renamed to `LegacyModelsToArrayTransformer`. `ModelsToArrayTransformer` should be only be used with SF2.7+
diff --git a/UPGRADE-3.x.md b/UPGRADE-3.x.md
index 892a122d77..8e0c264c58 100644
--- a/UPGRADE-3.x.md
+++ b/UPGRADE-3.x.md
@@ -1,8 +1,21 @@
UPGRADE 3.x
===========
-UPGRADE FROM 3.x to 3.x
-=======================
+## Deprecated `Sonata\AdminBundle\Model\ModelManagerInterface` collection-related methods.
+
+Use:
+- `new \Doctrine\Common\Collections\ArrayCollection()` instead of `getModelCollectionInstance($class)`
+- `$collection->removeElement($element)` instead of `collectionRemoveElement($collection, $element)`
+- `$collection->add($element)` instead of `collectionAddElement($collection, $element)`
+- `$collection->contains($element)` instead of `collectionHasElement($collection, $element)`
+- `$collection->clear()` instead of `collectionClear($collection)`
+
+UPGRADE FROM 3.73 to 3.74
+=========================
+
+## Deprecated `Sonata\AdminBundle\Datagrid\ProxyQueryInterface::getSingleScalarResult`
+
+Use `Sonata\AdminBundle\Datagrid\ProxyQueryInterface::execute` instead.
## The following templates have been deprecated
@@ -45,6 +58,10 @@ with `sonata-project/block-bundle` on your composer.json in order to avoid unwan
There is a minimal BC Break on `AdminListBlockService`, `AdminSearchBlockService` and `AdminStatsBlockService`. If you are extending those clases (keep in mind that they will become final on 4.0) you should add return type hints to `execute()` and `configureSettings()`.
+## Deprecated passing `callable` that does not return `Symfony\Component\Routing\Route` as `$element` (2nd argument) to `Sonata\AdminBundle\Route\RouteCollection::addElement($code, $element)`
+
+When calling a `Sonata\AdminBundle\Route\RouteCollection::addElement($code, $element)`, please pass `$element` of type `Route|callable():Route`. Passing `callable` that returns non instance of `Route` is deprecated.
+
UPGRADE FROM 3.72 to 3.73
=========================
diff --git a/UPGRADE-4.0.md b/UPGRADE-4.0.md
index 75af37cea1..16850aa621 100644
--- a/UPGRADE-4.0.md
+++ b/UPGRADE-4.0.md
@@ -128,6 +128,9 @@ If you have implemented a custom admin extension, you must adapt the signature o
* `configureBatchActions`
* `getAccessMapping`
+## AdminHelper
+The `AdminHelper::__construct` method changes its `Pool` param to a `PropertyAccessorInterface` one.
+
## BreadcrumbsBuilder
The `buildBreacrumbs` method may no longer be called from outside the class.
diff --git a/composer.json b/composer.json
index 5853d209c9..7e31bc8048 100644
--- a/composer.json
+++ b/composer.json
@@ -23,9 +23,11 @@
],
"require": {
"php": "^7.3",
- "doctrine/common": "^2.7",
+ "ext-json": "*",
+ "doctrine/collections": "^1.6",
+ "doctrine/common": "^2.7 || ^3.0",
"doctrine/inflector": "^1.4 || ^2.0",
- "doctrine/persistence": "^1.3.3",
+ "doctrine/persistence": "^1.3.6 || ^2.0",
"knplabs/knp-menu": "^3.1",
"knplabs/knp-menu-bundle": "^3.0",
"sonata-project/block-bundle": "^4.2",
@@ -70,7 +72,9 @@
},
"require-dev": {
"matthiasnoback/symfony-dependency-injection-test": "^4.1",
+ "phpdocumentor/reflection-docblock": "^5.2",
"phpstan/phpstan": "^0.12.29",
+ "psalm/plugin-symfony": "^1.4",
"psr/event-dispatcher": "^1.0",
"sonata-project/intl-bundle": "^2.4",
"symfony/browser-kit": "^4.4 || ^5.1",
@@ -78,7 +82,8 @@
"symfony/filesystem": "^4.4 || ^5.1",
"symfony/maker-bundle": "^1.17",
"symfony/phpunit-bridge": "^5.1.1",
- "symfony/yaml": "^4.4 || ^5.1"
+ "symfony/yaml": "^4.4 || ^5.1",
+ "vimeo/psalm": "^3.13.1"
},
"suggest": {
"kunicmarko/sonata-auto-configure-bundle": "Auto configures Admin classes",
diff --git a/docs/.doctor-rst.yaml b/docs/.doctor-rst.yaml
index 29257eb943..641228cde2 100644
--- a/docs/.doctor-rst.yaml
+++ b/docs/.doctor-rst.yaml
@@ -1,3 +1,7 @@
+# DO NOT EDIT THIS FILE!
+#
+# It's auto-generated by sonata-project/dev-kit package.
+
rules:
blank_line_after_directive: ~
short_array_syntax: ~
diff --git a/docs/conf.py b/docs/conf.py
index 66f99f3eee..09cf99d8ee 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -1,5 +1,9 @@
# -*- coding: utf-8 -*-
+
+# DO NOT EDIT THIS FILE!
#
+# It's auto-generated by sonata-project/dev-kit package.
+
# IoC documentation build configuration file, created by
# sphinx-quickstart on Fri Mar 29 01:43:00 2013.
#
@@ -44,7 +48,7 @@
master_doc = 'index'
# General information about the project.
-project = u''
+project = u'Sonata Admin Bundle'
copyright = u'2010-2019, Thomas Rabaix'
# The version info for the project you're documenting, acts as replacement for
diff --git a/docs/reference/routing.rst b/docs/reference/routing.rst
index caddc62cfb..a35f4adda0 100644
--- a/docs/reference/routing.rst
+++ b/docs/reference/routing.rst
@@ -281,7 +281,7 @@ Any single registered route can be removed by name::
Removing all routes except named ones
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-If you want to disable all default Sonata routes except few whitelisted ones, you can use
+If you want to disable all default Sonata routes except few allowed ones, you can use
the ``clearExcept()`` method. This method accepts an array of routes you want to keep active::
// src/Admin/MediaAdmin.php
diff --git a/docs/requirements.txt b/docs/requirements.txt
index a86ef50ae7..ee596d3c45 100644
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -1,6 +1,7 @@
# DO NOT EDIT THIS FILE!
#
# It's auto-generated by sonata-project/dev-kit package.
+
Sphinx==1.8.5
git+https://github.com/fabpot/sphinx-php.git
sphinx_rtd_theme
diff --git a/phpstan.neon.dist b/phpstan.neon.dist
index 0cce313b04..4e75f11fa4 100644
--- a/phpstan.neon.dist
+++ b/phpstan.neon.dist
@@ -2,7 +2,7 @@ includes:
- phpstan-baseline.neon
parameters:
- level: 4
+ level: 5
paths:
- src
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 01891c55df..36fc24d133 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -32,7 +32,7 @@ It's auto-generated by sonata-project/dev-kit package.
-
+
diff --git a/psalm-baseline.xml b/psalm-baseline.xml
new file mode 100644
index 0000000000..9231c5b801
--- /dev/null
+++ b/psalm-baseline.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+ DoctrinePersistentCollection
+
+
+
+
+
+ $namespace
+ $class_name
+ $fields
+ $fields
+ $fields
+ $fields
+
+
+
+
+ $namespace
+ $class_name
+
+
+
+
+ array_merge(array_flip($keys), $this->filters)
+
+
+
+
+ array_merge(array_flip($keys), $this->elements)
+
+
+
diff --git a/psalm.xml b/psalm.xml
new file mode 100644
index 0000000000..a02e46da29
--- /dev/null
+++ b/psalm.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Admin/AbstractAdmin.php b/src/Admin/AbstractAdmin.php
index 5772399e3a..11f4aafc88 100644
--- a/src/Admin/AbstractAdmin.php
+++ b/src/Admin/AbstractAdmin.php
@@ -580,11 +580,7 @@ public function update(object $object): object
$extension->preUpdate($this, $object);
}
- $result = $this->getModelManager()->update($object);
- // BC compatibility
- if (null !== $result) {
- $object = $result;
- }
+ $this->getModelManager()->update($object);
$this->postUpdate($object);
foreach ($this->extensions as $extension) {
@@ -601,11 +597,7 @@ public function create(object $object): object
$extension->prePersist($this, $object);
}
- $result = $this->getModelManager()->create($object);
- // BC compatibility
- if (null !== $result) {
- $object = $result;
- }
+ $this->getModelManager()->create($object);
$this->postPersist($object);
foreach ($this->extensions as $extension) {
@@ -1938,22 +1930,12 @@ public function getSecurityInformation(): array
*/
public function getPermissionsShow(string $context): array
{
- switch ($context) {
- case self::CONTEXT_DASHBOARD:
- case self::CONTEXT_MENU:
- default:
- return ['LIST'];
- }
+ return ['LIST'];
}
public function showIn(string $context): bool
{
- switch ($context) {
- case self::CONTEXT_DASHBOARD:
- case self::CONTEXT_MENU:
- default:
- return $this->isGranted($this->getPermissionsShow($context));
- }
+ return $this->isGranted($this->getPermissionsShow($context));
}
public function createObjectSecurity(object $object): void
@@ -2249,8 +2231,8 @@ final public function getActionButtons(string $action, ?object $object = null):
$buttonList = [];
if (\in_array($action, ['tree', 'show', 'edit', 'delete', 'list', 'batch'], true)
- && $this->hasAccess('create')
&& $this->hasRoute('create')
+ && $this->hasAccess('create')
) {
$buttonList['create'] = [
'template' => $this->getTemplateRegistry()->getTemplate('button_create'),
@@ -2258,8 +2240,8 @@ final public function getActionButtons(string $action, ?object $object = null):
}
if (\in_array($action, ['show', 'delete', 'acl', 'history'], true)
- && $this->canAccessObject('edit', $object)
&& $this->hasRoute('edit')
+ && $this->canAccessObject('edit', $object)
) {
$buttonList['edit'] = [
'template' => $this->getTemplateRegistry()->getTemplate('button_edit'),
@@ -2267,8 +2249,8 @@ final public function getActionButtons(string $action, ?object $object = null):
}
if (\in_array($action, ['show', 'edit', 'acl'], true)
- && $this->canAccessObject('history', $object)
&& $this->hasRoute('history')
+ && $this->canAccessObject('history', $object)
) {
$buttonList['history'] = [
'template' => $this->getTemplateRegistry()->getTemplate('button_history'),
@@ -2277,8 +2259,8 @@ final public function getActionButtons(string $action, ?object $object = null):
if (\in_array($action, ['edit', 'history'], true)
&& $this->isAclEnabled()
- && $this->canAccessObject('acl', $object)
&& $this->hasRoute('acl')
+ && $this->canAccessObject('acl', $object)
) {
$buttonList['acl'] = [
'template' => $this->getTemplateRegistry()->getTemplate('button_acl'),
@@ -2286,9 +2268,9 @@ final public function getActionButtons(string $action, ?object $object = null):
}
if (\in_array($action, ['edit', 'history', 'acl'], true)
+ && $this->hasRoute('show')
&& $this->canAccessObject('show', $object)
&& \count($this->getShow()) > 0
- && $this->hasRoute('show')
) {
$buttonList['show'] = [
'template' => $this->getTemplateRegistry()->getTemplate('button_show'),
@@ -2296,8 +2278,8 @@ final public function getActionButtons(string $action, ?object $object = null):
}
if (\in_array($action, ['show', 'edit', 'delete', 'acl', 'batch'], true)
- && $this->hasAccess('list')
&& $this->hasRoute('list')
+ && $this->hasAccess('list')
) {
$buttonList['list'] = [
'template' => $this->getTemplateRegistry()->getTemplate('button_list'),
diff --git a/src/Admin/AdminHelper.php b/src/Admin/AdminHelper.php
index 4eb936ffae..9c933a1a00 100644
--- a/src/Admin/AdminHelper.php
+++ b/src/Admin/AdminHelper.php
@@ -22,6 +22,7 @@
use Sonata\AdminBundle\Util\FormViewIterator;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormView;
+use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
/**
* @final since sonata-project/admin-bundle 3.52
@@ -36,13 +37,13 @@ class AdminHelper
private const FORM_FIELD_DELETE = '_delete';
/**
- * @var Pool
+ * @var PropertyAccessorInterface
*/
- protected $pool;
+ protected $propertyAccessor;
- public function __construct(Pool $pool)
+ public function __construct(PropertyAccessorInterface $propertyAccessor)
{
- $this->pool = $pool;
+ $this->propertyAccessor = $propertyAccessor;
}
/**
@@ -111,11 +112,9 @@ public function appendFormFieldElement(AdminInterface $admin, object $subject, s
//Child form not found (probably nested one)
//if childFormBuilder was not found resulted in fatal error getName() method call on non object
if (!$childFormBuilder) {
- $propertyAccessor = $this->pool->getPropertyAccessor();
-
$path = $this->getElementAccessPath($elementId, $subject);
- $collection = $propertyAccessor->getValue($subject, $path);
+ $collection = $this->propertyAccessor->getValue($subject, $path);
if ($collection instanceof DoctrinePersistentCollection || $collection instanceof PersistentCollection) {
//since doctrine 2.4
@@ -127,7 +126,7 @@ public function appendFormFieldElement(AdminInterface $admin, object $subject, s
}
$collection->add(new $modelClassName());
- $propertyAccessor->setValue($subject, $path, $collection);
+ $this->propertyAccessor->setValue($subject, $path, $collection);
$fieldDescription = null;
} else {
@@ -195,8 +194,6 @@ public function appendFormFieldElement(AdminInterface $admin, object $subject, s
*/
public function getElementAccessPath(string $elementId, $model): string
{
- $propertyAccessor = $this->pool->getPropertyAccessor();
-
$idWithoutIdentifier = preg_replace('/^[^_]*_/', '', $elementId);
$initialPath = preg_replace('#(_(\d+)_)#', '[$2]_', $idWithoutIdentifier);
@@ -208,7 +205,7 @@ public function getElementAccessPath(string $elementId, $model): string
$currentPath .= empty($currentPath) ? $part : '_'.$part;
$separator = empty($totalPath) ? '' : '.';
- if ($propertyAccessor->isReadable($model, $totalPath.$separator.$currentPath)) {
+ if ($this->propertyAccessor->isReadable($model, $totalPath.$separator.$currentPath)) {
$totalPath .= $separator.$currentPath;
$currentPath = '';
}
diff --git a/src/Admin/AdminInterface.php b/src/Admin/AdminInterface.php
index 87c29f4496..44ddd1a945 100644
--- a/src/Admin/AdminInterface.php
+++ b/src/Admin/AdminInterface.php
@@ -72,6 +72,8 @@ public function attachAdminClass(FieldDescriptionInterface $fieldDescription): v
public function getDatagrid(): DatagridInterface;
+ public function getPagerType(): string;
+
/**
* Set base controller name.
*/
@@ -162,7 +164,7 @@ public function setSecurityHandler(SecurityHandlerInterface $securityHandler): v
public function getSecurityHandler(): ?SecurityHandlerInterface;
/**
- * @param string $name
+ * @param string|array $name
*/
public function isGranted($name, ?object $object = null): bool;
diff --git a/src/Command/GenerateObjectAclCommand.php b/src/Command/GenerateObjectAclCommand.php
index 2628114a38..665b6e3013 100644
--- a/src/Command/GenerateObjectAclCommand.php
+++ b/src/Command/GenerateObjectAclCommand.php
@@ -13,7 +13,7 @@
namespace Sonata\AdminBundle\Command;
-use Doctrine\Common\Persistence\ManagerRegistry;
+use Doctrine\Persistence\ManagerRegistry;
use Sonata\AdminBundle\Admin\AdminInterface;
use Sonata\AdminBundle\Admin\Pool;
use Sonata\AdminBundle\Util\ObjectAclManipulatorInterface;
diff --git a/src/Controller/CRUDController.php b/src/Controller/CRUDController.php
index 33c5c194dd..5da8a567f7 100644
--- a/src/Controller/CRUDController.php
+++ b/src/Controller/CRUDController.php
@@ -470,16 +470,22 @@ public function batchAction(Request $request)
$confirmation = $request->get('confirmation', false);
- if ($data = json_decode($request->get('data', ''), true)) {
+ $forwardedRequest = $request->duplicate();
+
+ if ($data = json_decode((string) $request->get('data', ''), true)) {
$action = $data['action'];
$idx = $data['idx'];
$allElements = (bool) $data['all_elements'];
- $request->request->replace(array_merge($request->request->all(), $data));
+ $forwardedRequest->request->replace(array_merge($forwardedRequest->request->all(), $data));
} else {
- $action = $request->request->getAlnum('action');
+ $action = $forwardedRequest->request->getAlnum('action');
$idx = $request->request->get('idx', []);
- $allElements = $request->request->getBoolean('all_elements');
- $data = $request->request->all();
+ $allElements = $forwardedRequest->request->getBoolean('all_elements');
+
+ $forwardedRequest->request->set('idx', $idx);
+ $forwardedRequest->request->set('all_elements', $allElements);
+
+ $data = $forwardedRequest->request->all();
unset($data['_sonata_csrf_token']);
}
@@ -493,7 +499,7 @@ public function batchAction(Request $request)
$isRelevantAction = sprintf('batchAction%sIsRelevant', $camelizedAction);
if (method_exists($this, $isRelevantAction)) {
- $nonRelevantMessage = $this->$isRelevantAction($idx, $allElements, $request);
+ $nonRelevantMessage = $this->$isRelevantAction($idx, $allElements, $forwardedRequest);
} else {
$nonRelevantMessage = 0 !== \count($idx) || $allElements; // at least one item is selected
}
@@ -564,7 +570,7 @@ public function batchAction(Request $request)
return $this->redirectToList();
}
- return $this->$finalAction($query, $request);
+ return $this->$finalAction($query, $forwardedRequest);
}
/**
diff --git a/src/Datagrid/Datagrid.php b/src/Datagrid/Datagrid.php
index e1e0ef5478..d92dc2661b 100644
--- a/src/Datagrid/Datagrid.php
+++ b/src/Datagrid/Datagrid.php
@@ -199,9 +199,11 @@ static function ($value) {
$this->bound = true;
}
- public function addFilter(FilterInterface $filter): void
+ public function addFilter(FilterInterface $filter): FilterInterface
{
$this->filters[$filter->getName()] = $filter;
+
+ return $filter;
}
public function hasFilter(string $name): bool
diff --git a/src/Datagrid/DatagridInterface.php b/src/Datagrid/DatagridInterface.php
index a401a19dd3..fcdf80ed54 100644
--- a/src/Datagrid/DatagridInterface.php
+++ b/src/Datagrid/DatagridInterface.php
@@ -34,7 +34,7 @@ public function getResults(): array;
public function buildPager(): void;
- public function addFilter(FilterInterface $filter): void;
+ public function addFilter(FilterInterface $filter): FilterInterface;
/**
* @return array
diff --git a/src/Datagrid/Pager.php b/src/Datagrid/Pager.php
index 8f42717b2e..73113856a3 100644
--- a/src/Datagrid/Pager.php
+++ b/src/Datagrid/Pager.php
@@ -420,6 +420,7 @@ public function next()
--$this->resultsCounter;
+ // NEXT_MAJOR: remove `return` statement, to be compatible with Iterator::next(): void
return next($this->results);
}
@@ -434,6 +435,7 @@ public function rewind()
$this->resultsCounter = \count($this->results);
+ // NEXT_MAJOR: remove `return` statement, to be compatible with Iterator::rewind(): void
return reset($this->results);
}
diff --git a/src/Datagrid/ProxyQueryInterface.php b/src/Datagrid/ProxyQueryInterface.php
index 6699d3d647..c69a4264cf 100644
--- a/src/Datagrid/ProxyQueryInterface.php
+++ b/src/Datagrid/ProxyQueryInterface.php
@@ -40,17 +40,24 @@ public function execute(array $params = [], ?int $hydrationMode = null);
*/
public function setSortBy(array $parentAssociationMappings, array $fieldMapping): self;
- public function getSortBy(): string;
+ public function getSortBy(): ?string;
public function setSortOrder(string $sortOrder): self;
- public function getSortOrder(): string;
+ public function getSortOrder(): ?string;
- public function getSingleScalarResult(): ?int;
+ /**
+ * NEXT_MAJOR: Remove this method.
+ *
+ * @deprecated since sonata-project/admin-bundle 3.74, to be removed in 4.0.
+ *
+ * @return mixed
+ */
+ public function getSingleScalarResult();
public function setFirstResult(?int $firstResult): self;
- public function getFirstResult(): ?object;
+ public function getFirstResult(): ?int;
public function setMaxResults(?int $maxResults): self;
@@ -59,6 +66,8 @@ public function getMaxResults(): ?int;
public function getUniqueParameterId(): int;
/**
+ * Join entities from the given association mappings and return the last alias created.
+ *
* @param mixed[] $associationMappings
*/
public function entityJoin(array $associationMappings): string;
diff --git a/src/Form/ChoiceList/ModelChoiceLoader.php b/src/Form/ChoiceList/ModelChoiceLoader.php
index e86f2cf6ad..c4cd4fd07a 100644
--- a/src/Form/ChoiceList/ModelChoiceLoader.php
+++ b/src/Form/ChoiceList/ModelChoiceLoader.php
@@ -28,8 +28,6 @@
*/
final class ModelChoiceLoader implements ChoiceLoaderInterface
{
- public $identifier;
-
/**
* @var \Sonata\AdminBundle\Model\ModelManagerInterface
*/
@@ -78,8 +76,6 @@ public function __construct(
$this->query = $query;
$this->choices = $choices;
- $this->identifier = $this->modelManager->getIdentifierFieldNames($this->class);
-
// The property option defines, which property (path) is used for
// displaying entities as strings
if ($property) {
diff --git a/src/Form/DataTransformer/ModelToIdPropertyTransformer.php b/src/Form/DataTransformer/ModelToIdPropertyTransformer.php
index a41ebf3957..e763988d1e 100644
--- a/src/Form/DataTransformer/ModelToIdPropertyTransformer.php
+++ b/src/Form/DataTransformer/ModelToIdPropertyTransformer.php
@@ -13,6 +13,7 @@
namespace Sonata\AdminBundle\Form\DataTransformer;
+use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Util\ClassUtils;
use Sonata\AdminBundle\Model\ModelManagerInterface;
use Symfony\Component\Form\DataTransformerInterface;
@@ -71,7 +72,8 @@ public function __construct(
public function reverseTransform($value)
{
- $collection = $this->modelManager->getModelCollectionInstance($this->className);
+ /** @var ArrayCollection $collection */
+ $collection = new ArrayCollection();
if (empty($value)) {
if ($this->multiple) {
@@ -94,7 +96,11 @@ public function reverseTransform($value)
continue;
}
- $collection[] = $this->modelManager->find($this->className, $id);
+ $object = $this->modelManager->find($this->className, $id);
+
+ if (null !== $object) {
+ $collection->add($object);
+ }
}
return $collection;
@@ -155,15 +161,13 @@ public function transform($entityOrCollection)
}
$label = ($this->toStringCallback)($model, $this->property);
+ } elseif (method_exists($model, '__toString')) {
+ $label = (string) $model;
} else {
- try {
- $label = (string) $model;
- } catch (\Exception $e) {
- throw new \RuntimeException(sprintf(
- 'Unable to convert the entity %s to String, entity must have a \'__toString()\' method defined',
- ClassUtils::getClass($model)
- ), 0, $e);
- }
+ throw new \RuntimeException(sprintf(
+ 'Unable to convert the entity %s to String, entity must have a \'__toString()\' method defined',
+ ClassUtils::getClass($model)
+ ));
}
$result[] = $id;
diff --git a/src/Form/DataTransformer/ModelsToArrayTransformer.php b/src/Form/DataTransformer/ModelsToArrayTransformer.php
index d899cde5bd..32ff9038f6 100644
--- a/src/Form/DataTransformer/ModelsToArrayTransformer.php
+++ b/src/Form/DataTransformer/ModelsToArrayTransformer.php
@@ -13,6 +13,7 @@
namespace Sonata\AdminBundle\Form\DataTransformer;
+use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Util\ClassUtils;
use Sonata\AdminBundle\Model\ModelManagerInterface;
use Sonata\Doctrine\Adapter\AdapterInterface;
@@ -67,13 +68,14 @@ public function reverseTransform($keys)
throw new UnexpectedTypeException($keys, 'array');
}
- $collection = $this->modelManager->getModelCollectionInstance($this->class);
+ /** @var ArrayCollection $collection */
+ $collection = new ArrayCollection();
$notFound = [];
// optimize this into a SELECT WHERE IN query
foreach ($keys as $key) {
if ($model = $this->modelManager->find($this->class, $key)) {
- $collection[] = $model;
+ $collection->add($model);
} else {
$notFound[] = $key;
}
diff --git a/src/Form/EventListener/MergeCollectionListener.php b/src/Form/EventListener/MergeCollectionListener.php
index 1055b0dcc1..ec78232ac6 100644
--- a/src/Form/EventListener/MergeCollectionListener.php
+++ b/src/Form/EventListener/MergeCollectionListener.php
@@ -13,6 +13,7 @@
namespace Sonata\AdminBundle\Form\EventListener;
+use Doctrine\Common\Collections\Collection;
use Sonata\AdminBundle\Model\ModelManagerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\FormEvent;
@@ -24,12 +25,23 @@
final class MergeCollectionListener implements EventSubscriberInterface
{
/**
- * @var ModelManagerInterface
+ * @var ModelManagerInterface|null
*/
private $modelManager;
- public function __construct(ModelManagerInterface $modelManager)
+ /**
+ * NEXT_MAJOR: Remove this constructor and the modelManager property.
+ */
+ public function __construct(?ModelManagerInterface $modelManager = null)
{
+ if (null !== $modelManager) {
+ @trigger_error(sprintf(
+ 'Passing argument 1 to %s() is deprecated since sonata-project/admin-bundle 3.x'
+ .' and will be ignored in version 4.0.',
+ __METHOD__
+ ), E_USER_DEPRECATED);
+ }
+
$this->modelManager = $modelManager;
}
@@ -43,7 +55,10 @@ public static function getSubscribedEvents()
public function onBind(FormEvent $event): void
{
$collection = $event->getForm()->getData();
+ \assert(null === $collection || $collection instanceof Collection);
+
$data = $event->getData();
+ \assert($data instanceof Collection);
// looks like there is no way to remove other listeners
$event->stopPropagation();
@@ -51,19 +66,19 @@ public function onBind(FormEvent $event): void
if (!$collection) {
$collection = $data;
} elseif (0 === \count($data)) {
- $this->modelManager->collectionClear($collection);
+ $collection->clear();
} else {
// merge $data into $collection
foreach ($collection as $model) {
- if (!$this->modelManager->collectionHasElement($data, $model)) {
- $this->modelManager->collectionRemoveElement($collection, $model);
+ if (!$data->contains($model)) {
+ $collection->removeElement($model);
} else {
- $this->modelManager->collectionRemoveElement($data, $model);
+ $data->removeElement($model);
}
}
foreach ($data as $model) {
- $this->modelManager->collectionAddElement($collection, $model);
+ $collection->add($model);
}
}
diff --git a/src/Maker/AdminMaker.php b/src/Maker/AdminMaker.php
index b489a5a51b..d0f8f54aca 100644
--- a/src/Maker/AdminMaker.php
+++ b/src/Maker/AdminMaker.php
@@ -39,7 +39,7 @@ final class AdminMaker extends AbstractMaker
private $projectDirectory;
/**
- * @var string[]
+ * @var array
*/
private $availableModelManagers;
@@ -78,6 +78,10 @@ final class AdminMaker extends AbstractMaker
*/
private $modelManager;
+ /**
+ * @param string $projectDirectory
+ * @param array $modelManagers
+ */
public function __construct($projectDirectory, array $modelManagers = [])
{
$this->projectDirectory = $projectDirectory;
@@ -300,6 +304,6 @@ private function configure(InputInterface $input): void
}
$this->managerType = $input->getOption('manager') ?: array_keys($this->availableModelManagers)[0];
- $this->modelManager = $this->availableModelManagers[$this->managerType] ?: current($this->availableModelManagers);
+ $this->modelManager = $this->availableModelManagers[$this->managerType] ?? current($this->availableModelManagers);
}
}
diff --git a/src/Manipulator/ObjectManipulator.php b/src/Manipulator/ObjectManipulator.php
index 9b4a5e1fd2..5e4634600a 100644
--- a/src/Manipulator/ObjectManipulator.php
+++ b/src/Manipulator/ObjectManipulator.php
@@ -53,7 +53,7 @@ public static function setObject(
object $object,
FieldDescriptionInterface $parentFieldDescription
): object {
- $mappedBy = $parentFieldDescription->getAssociationMapping()['mappedBy'];
+ $mappedBy = $parentFieldDescription->getAssociationMapping()['mappedBy'] ?? null;
if (null === $mappedBy) {
return $instance;
}
diff --git a/src/Model/AuditManager.php b/src/Model/AuditManager.php
index 9442bd454e..e3668b3e28 100644
--- a/src/Model/AuditManager.php
+++ b/src/Model/AuditManager.php
@@ -21,12 +21,8 @@
final class AuditManager implements AuditManagerInterface
{
/**
- * @var array
- */
- private $classes = [];
-
- /**
- * @var array
+ * @var array
+ * @phpstan-var array
*/
private $readers = [];
@@ -40,12 +36,12 @@ public function __construct(ContainerInterface $container)
$this->container = $container;
}
- public function setReader($serviceId, array $classes): void
+ public function setReader(string $serviceId, array $classes): void
{
$this->readers[$serviceId] = $classes;
}
- public function hasReader($class)
+ public function hasReader(string $class): bool
{
foreach ($this->readers as $classes) {
if (\in_array($class, $classes, true)) {
@@ -56,7 +52,7 @@ public function hasReader($class)
return false;
}
- public function getReader($class)
+ public function getReader(string $class): AuditReaderInterface
{
foreach ($this->readers as $readerId => $classes) {
if (\in_array($class, $classes, true)) {
diff --git a/src/Model/AuditManagerInterface.php b/src/Model/AuditManagerInterface.php
index 878d6d3acf..2cd71f33af 100644
--- a/src/Model/AuditManagerInterface.php
+++ b/src/Model/AuditManagerInterface.php
@@ -21,27 +21,25 @@ interface AuditManagerInterface
/**
* Set AuditReaderInterface service id for array of $classes.
*
- * @param string $serviceId
+ * @param string[] $classes
+ *
+ * @phpstan-param class-string[] $classes
*/
- public function setReader($serviceId, array $classes);
+ public function setReader(string $serviceId, array $classes): void;
/**
* Returns true if $class has AuditReaderInterface.
*
- * @param string $class
- *
- * @return bool
+ * @phpstan-param class-string $class
*/
- public function hasReader($class);
+ public function hasReader(string $class): bool;
/**
* Get AuditReaderInterface service for $class.
*
- * @param string $class
- *
* @throws \LogicException
*
- * @return AuditReaderInterface
+ * @phpstan-param class-string $class
*/
- public function getReader($class);
+ public function getReader(string $class): AuditReaderInterface;
}
diff --git a/src/Model/AuditReaderInterface.php b/src/Model/AuditReaderInterface.php
index 83338353c4..c7a2629cd7 100644
--- a/src/Model/AuditReaderInterface.php
+++ b/src/Model/AuditReaderInterface.php
@@ -19,46 +19,47 @@
interface AuditReaderInterface
{
/**
- * @param string $className
- * @param string $id
- * @param string $revision
+ * @template T of object
*
- * @return object
+ * @param mixed $id
+ * @param mixed $revisionId
+ *
+ * @phpstan-param class-string $className
+ * @phpstan-return T|null
*/
- public function find($className, $id, $revision);
+ public function find(string $className, $id, $revisionId): ?object;
/**
- * @param string $className
- * @param int $limit
- * @param int $offset
- *
* @return object[]
+ *
+ * @phpstan-param class-string $className
*/
- public function findRevisionHistory($className, $limit = 20, $offset = 0);
+ public function findRevisionHistory(string $className, int $limit = 20, int $offset = 0): array;
/**
- * @param string $classname
- * @param string $revision
+ * @param mixed $revisionId
*
- * @return object
+ * @phpstan-param class-string $className
*/
- public function findRevision($classname, $revision);
+ public function findRevision(string $className, $revisionId): ?object;
/**
- * @param string $className
- * @param string $id
+ * @param mixed $id
*
* @return object[]
+ *
+ * @phpstan-param class-string $className
*/
- public function findRevisions($className, $id);
+ public function findRevisions(string $className, $id): array;
/**
- * @param string $className
- * @param int $id
- * @param int $oldRevision
- * @param int $newRevision
+ * @param mixed $id
+ * @param mixed $oldRevisionId
+ * @param mixed $newRevisionId
+ *
+ * @return array
*
- * @return array
+ * @phpstan-param class-string $className
*/
- public function diff($className, $id, $oldRevision, $newRevision);
+ public function diff(string $className, $id, $oldRevisionId, $newRevisionId): array;
}
diff --git a/src/Model/DatagridManagerInterface.php b/src/Model/DatagridManagerInterface.php
index 822f749102..104d37491c 100644
--- a/src/Model/DatagridManagerInterface.php
+++ b/src/Model/DatagridManagerInterface.php
@@ -19,18 +19,18 @@
interface DatagridManagerInterface
{
/**
- * Return _sort_order, _sort_by, _page and _per_page values.
+ * @return array{_page?: int, _per_page?: int, _sort_by?: string, _sort_order?: string}
*
- * @param string $class
- *
- * @return array
+ * @phpstan-param class-string $class
*/
- public function getDefaultSortValues($class);
+ public function getDefaultSortValues(string $class): array;
/**
* Return all the allowed _per_page values.
*
- * @return array
+ * @return int[]
+ *
+ * @phpstan-param class-string $class
*/
public function getDefaultPerPageOptions(string $class): array;
}
diff --git a/src/Model/LockInterface.php b/src/Model/LockInterface.php
index 5ce55afb07..c03009b142 100644
--- a/src/Model/LockInterface.php
+++ b/src/Model/LockInterface.php
@@ -21,17 +21,12 @@
interface LockInterface extends ModelManagerInterface
{
/**
- * @param object $object
- *
- * @return mixed|null
+ * @return mixed
*/
- public function getLockVersion($object);
+ public function getLockVersion(object $object);
/**
- * @param object $object
- * @param mixed $expectedVersion
- *
* @throws LockException
*/
- public function lock($object, $expectedVersion);
+ public function lock(object $object, ?int $expectedVersion): void;
}
diff --git a/src/Model/ModelManagerInterface.php b/src/Model/ModelManagerInterface.php
index bc5b7c32c2..2bbd1758b5 100644
--- a/src/Model/ModelManagerInterface.php
+++ b/src/Model/ModelManagerInterface.php
@@ -13,6 +13,7 @@
namespace Sonata\AdminBundle\Model;
+use Doctrine\Common\Collections\Collection;
use Sonata\AdminBundle\Admin\FieldDescriptionInterface;
use Sonata\AdminBundle\Datagrid\DatagridInterface;
use Sonata\AdminBundle\Datagrid\ProxyQueryInterface;
@@ -25,70 +26,74 @@
interface ModelManagerInterface extends DatagridManagerInterface
{
/**
- * @param string $class
- * @param string $name
+ * @param array $options
*
- * @return FieldDescriptionInterface
+ * @phpstan-param class-string $class
*/
- public function getNewFieldDescriptionInstance($class, $name, array $options = []);
+ public function getNewFieldDescriptionInstance(string $class, string $name, array $options = []): FieldDescriptionInterface;
/**
- * @param object $object
- *
* @throws ModelManagerException
*/
- public function create($object);
+ public function create(object $object): void;
/**
- * @param object $object
- *
* @throws ModelManagerException
*/
- public function update($object);
+ public function update(object $object): void;
/**
- * @param object $object
- *
* @throws ModelManagerException
*/
- public function delete($object);
+ public function delete(object $object): void;
/**
- * @param string $class
+ * @template T of object
+ *
+ * @param array $criteria
+ *
+ * @return object[] all objects matching the criteria
*
- * @return array all objects matching the criteria
+ * @phpstan-param class-string $class
+ * @phpstan-return T[]
*/
- public function findBy($class, array $criteria = []);
+ public function findBy(string $class, array $criteria = []): array;
/**
- * @param string $class
+ * @template T of object
+ *
+ * @param array $criteria
*
* @return object|null an object matching the criteria or null if none match
+ *
+ * @phpstan-param class-string $class
+ * @phpstan-return T|null
*/
- public function findOneBy($class, array $criteria = []);
+ public function findOneBy(string $class, array $criteria = []): ?object;
/**
- * @param string $class
- * @param mixed $id
+ * @template T of object
+ *
+ * @param mixed $id
*
* @return object|null the object with id or null if not found
+ *
+ * @phpstan-param class-string $class
+ * @phpstan-return T|null
*/
- public function find($class, $id);
+ public function find(string $class, $id): ?object;
/**
- * @param string $class
- *
* @throws ModelManagerException
+ *
+ * @phpstan-param class-string $class
*/
- public function batchDelete($class, ProxyQueryInterface $queryProxy);
+ public function batchDelete(string $class, ProxyQueryInterface $queryProxy): void;
/**
- * @param string $class
- * @param string $alias
- *
- * @return ProxyQueryInterface
+ * @phpstan-param class-string $class
*/
- public function createQuery($class, $alias = 'o');
+ public function createQuery(string $class, string $alias = 'o'): ProxyQueryInterface;
/**
* Get the identifiers of this model class.
@@ -97,135 +102,159 @@ public function createQuery($class, $alias = 'o');
* composed of multiple columns. If you need a string representation,
* use getNormalizedIdentifier resp. getUrlSafeIdentifier
*
- * @param object $model
- *
- * @return array list of all identifiers of this model
+ * @return mixed[]
*/
- public function getIdentifierValues($model);
+ public function getIdentifierValues(object $model): array;
/**
- * Get a list of the field names models of the specified class use to store
- * the identifier.
+ * Get a list of the field names models of the specified fully qualified
+ * class name used to store the identifier.
*
- * @param string $class fully qualified class name
+ * @return string[]
*
- * @return array
+ * @phpstan-param class-string $class
*/
- public function getIdentifierFieldNames($class);
+ public function getIdentifierFieldNames(string $class): array;
/**
* Get the identifiers for this model class as a string.
- *
- * @param object $model
- *
- * @return string a string representation of the identifiers for this
- * instance
*/
- public function getNormalizedIdentifier($model);
+ public function getNormalizedIdentifier(object $model): string;
/**
* Get the identifiers as a string that is safe to use in a url.
*
* This is similar to getNormalizedIdentifier but guarantees an id that can
* be used in a URL.
- *
- * @param object $model
- *
- * @return string string representation of the id that is safe to use in a url
*/
- public function getUrlSafeIdentifier($model);
+ public function getUrlSafeIdentifier(object $model): string;
/**
* Create a new instance of the model of the specified class.
*
- * @param string $class
+ * @template T of object
*
- * @return object
+ * @phpstan-param class-string $class
+ * @phpstan-return T
*/
- public function getModelInstance($class);
+ public function getModelInstance(string $class): object;
/**
- * @param string $class
+ * NEXT_MAJOR: Remove this method.
+ *
+ * @deprecated since sonata-project/admin-bundle 3.x. To be removed in 4.0. Use doctrine/collections instead.
*
- * @return array|\ArrayAccess
+ * @template T of object
+ *
+ * @return Collection
+ *
+ * @phpstan-param class-string $class
+ * @phpstan-return Collection
*/
- public function getModelCollectionInstance($class);
+ public function getModelCollectionInstance(string $class): Collection;
/**
+ * NEXT_MAJOR: Remove this method.
+ *
+ * @deprecated since sonata-project/admin-bundle 3.x. To be removed in 4.0. Use doctrine/collections instead.
+ *
* Removes an element from the collection.
*
- * @param array $collection
- * @param object $element
+ * @template T of object
+ *
+ * @param Collection $collection
+ *
+ * @phpstan-param Collection $collection
+ * @phpstan-param T $element
*/
- public function collectionRemoveElement(&$collection, &$element);
+ public function collectionRemoveElement(Collection $collection, object $element): void;
/**
+ * NEXT_MAJOR: Remove this method.
+ *
+ * @deprecated since sonata-project/admin-bundle 3.x. To be removed in 4.0. Use doctrine/collections instead.
+ *
* Add an element from the collection.
*
- * @param array $collection
- * @param object $element
+ * @template T of object
+ *
+ * @param Collection $collection
+ *
+ * @phpstan-param Collection $collection
+ * @phpstan-param T $element
*/
- public function collectionAddElement(&$collection, &$element);
+ public function collectionAddElement(Collection $collection, object $element): void;
/**
+ * NEXT_MAJOR: Remove this method.
+ *
+ * @deprecated since sonata-project/admin-bundle 3.x. To be removed in 4.0. Use doctrine/collections instead.
+ *
* Check if the element exists in the collection.
*
- * @param array $collection
- * @param object $element
+ * @template T of object
+ *
+ * @param Collection $collection
*
- * @return bool
+ * @phpstan-param Collection $collection
+ * @phpstan-param T $element
*/
- public function collectionHasElement(&$collection, &$element);
+ public function collectionHasElement(Collection $collection, object $element): bool;
/**
* Clear the collection.
*
- * @param array $collection
+ * @param Collection $collection
+ *
+ * @phpstan-param Collection $collection
*/
- public function collectionClear(&$collection);
+ public function collectionClear(Collection $collection): void;
/**
- * @param string $class
+ * @template T of object
+ *
+ * @param array $array
*
- * @return object
+ * @phpstan-param class-string $class
+ * @phpstan-return T
*/
- public function modelReverseTransform($class, array $array = []);
+ public function modelReverseTransform(string $class, array $array = []): object;
/**
- * @param string $class
- * @param object $instance
+ * @template T of object
*
- * @return object
+ * @phpstan-param class-string $class
+ * @phpstan-param T $instance
+ * @phpstan-return T
*/
- public function modelTransform($class, $instance);
+ public function modelTransform(string $class, object $instance): object;
/**
- * @param mixed $query
+ * @return mixed
*/
- public function executeQuery($query);
+ public function executeQuery(object $query);
/**
- * @param int|null $firstResult
- * @param int|null $maxResult
- *
- * @return SourceIteratorInterface
+ * @param string[] $fields
*/
public function getDataSourceIterator(
DatagridInterface $datagrid,
array $fields,
- $firstResult = null,
- $maxResult = null
- );
+ ?int $firstResult = null,
+ ?int $maxResult = null
+ ): SourceIteratorInterface;
/**
- * @param string $class
- *
* @return string[]
+ *
+ * @phpstan-param class-string $class
*/
- public function getExportFields($class);
+ public function getExportFields(string $class): array;
/**
- * @param string $class
+ * @param string[] $idx
+ *
+ * @phpstan-param class-string $class
*/
- public function addIdentifiersToQuery($class, ProxyQueryInterface $query, array $idx);
+ public function addIdentifiersToQuery(string $class, ProxyQueryInterface $query, array $idx): void;
}
diff --git a/src/Resources/config/core.xml b/src/Resources/config/core.xml
index b5ec5438e3..4fcd723b6c 100644
--- a/src/Resources/config/core.xml
+++ b/src/Resources/config/core.xml
@@ -18,7 +18,7 @@
-
+
@@ -74,7 +74,6 @@
-
%sonata.admin.configuration.global_search.case_sensitive%
diff --git a/src/Resources/views/CRUD/base_list.html.twig b/src/Resources/views/CRUD/base_list.html.twig
index cff1e0e624..f5cee2f188 100644
--- a/src/Resources/views/CRUD/base_list.html.twig
+++ b/src/Resources/views/CRUD/base_list.html.twig
@@ -192,7 +192,7 @@ file that was distributed with this source code.