Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 8763757

Browse filesBrowse files
committed
[PropertyAccess] Custom methods on property accesses
1 parent 10ea5e9 commit 8763757
Copy full SHA for 8763757

File tree

1 file changed

+274
-1
lines changed
Filter options

1 file changed

+274
-1
lines changed

‎components/property_access/introduction.rst

Copy file name to clipboardExpand all lines: components/property_access/introduction.rst
+274-1Lines changed: 274 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,9 @@ property name (``first_name`` becomes ``FirstName``) and prefixes it with
116116

117117
var_dump($accessor->getValue($person, 'first_name')); // 'Wouter'
118118

119+
You can override the called getter method using metadata (i.e. annotations or
120+
configuration files). see `Custom method calls and virtual properties in a class`_
121+
119122
Using Hassers/Issers
120123
~~~~~~~~~~~~~~~~~~~~
121124

@@ -306,6 +309,9 @@ see `Enable other Features`_.
306309
307310
var_dump($person->getWouter()); // array(...)
308311
312+
You can override the called setter method using metadata (i.e. annotations or
313+
configuration files). see `Custom method calls and virtual properties in a class`_
314+
309315
Checking Property Paths
310316
-----------------------
311317

@@ -365,8 +371,225 @@ You can also mix objects and arrays::
365371
var_dump('Hello '.$accessor->getValue($person, 'children[0].firstName')); // 'Wouter'
366372
// equal to $person->getChildren()[0]->firstName
367373

374+
Custom method calls and virtual properties in a class
375+
-----------------------------------------------------
376+
377+
Sometimes you may not want the component to guess which method has to be called
378+
when reading or writing properties. This is specially interesting when property
379+
names are not in English or its singularization is not properly detected.
380+
381+
For those cases you can add metadata to the class being accessed so that the
382+
component will use a particular method as a getter, setter or even adder and
383+
remover (for collections).
384+
385+
Another interesting use of custom methods is declaring virtual properties
386+
which are not stored directly in the object.
387+
388+
There are three supported ways to state this metadata supported out-of-the-box by
389+
the component: using annotations, using YAML configuration files or using XML
390+
configuration files.
391+
392+
.. caution::
393+
394+
When using as a standalone component the metadata feature is disabled by
395+
default. You can enable it by calling
396+
:method:`PropertyAccessorBuilder::setMetadataFactory
397+
<Symfony\\Component\\PropertyAccess\\PropertyAccessorBuilder::setMetadataFactory>`
398+
see `Enable other Features`_.
399+
400+
There are four method calls that can be overriden: `getter`, `setter`, `adder` and
401+
`remover`.
402+
403+
When using annotations you can precede a property with `@Property` to state which
404+
method should be called when a get, set, add or remove operation is needed on the
405+
property.
406+
407+
.. configuration-block::
408+
409+
.. code-block:: php
410+
411+
// ...
412+
use Symfony\Component\PropertyAccess\Annotation\Property;
413+
414+
class Person
415+
{
416+
/**
417+
* @Property(getter="getFullName", setter="setFullName")
418+
*/
419+
private $name;
420+
421+
/**
422+
* @Property(adder="addNewChild", remover="discardChild")
423+
*/
424+
private $children;
425+
426+
public function getFullName()
427+
{
428+
return $this->name;
429+
}
430+
431+
public function setFullName($fullName)
432+
{
433+
$this->name = $fullName;
434+
}
435+
}
436+
437+
.. code-block:: yaml
438+
439+
Person:
440+
name:
441+
getter: getFullName
442+
setter: setFullName
443+
children:
444+
adder: addNewChild
445+
remover: discardChild
446+
447+
.. code-block:: xml
448+
449+
<?xml version="1.0" ?>
450+
451+
<property-access xmlns="http://symfony.com/schema/dic/property-access-mapping"
452+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
453+
xsi:schemaLocation="http://symfony.com/schema/dic/property-access-mapping http://symfony.com/schema/dic/property-access-mapping/property-access-mapping-1.0.xsd">
454+
455+
<class name="Person">
456+
<property name="name" getter="getFullName" setter="setFullName" />
457+
<property name="children" adder="addNewChild" remover="discardChild" />
458+
</class>
459+
460+
</property-access>
461+
462+
Then, using the overriden methods is automatic:
463+
464+
.. code-block:: php
465+
466+
$person = new Person();
467+
468+
$accessor->setValue($person, 'name', 'John Doe');
469+
// will call setFullName
470+
471+
var_dump('Hello '.$accesor->getValue($person, 'name'));
472+
// will return 'Hello John Doe'
473+
474+
You can also associate a particular method with an operation on a property
475+
using the `@Getter`, `@Setter`, `@Adder` and `@Remover` annotations. All of them
476+
take only one parameter: `property`.
477+
478+
This allows creating virtual properties that are not directly stored in the
479+
object::
480+
481+
.. configuration-block::
482+
483+
.. code-block:: php
484+
485+
// ...
486+
use Symfony\Component\PropertyAccess\Annotation\Getter;
487+
use Symfony\Component\PropertyAccess\Annotation\Setter;
488+
489+
class Invoice
490+
{
491+
private $quantity;
492+
493+
private $pricePerUnit;
494+
495+
// Notice that there is no real "total" property
496+
497+
/**
498+
* @Getter(property="total")
499+
*/
500+
public function getTotal()
501+
{
502+
return $this->quantity * $this->pricePerUnit;
503+
}
504+
505+
/**
506+
* @Setter(property="total")
507+
*
508+
* @param mixed $total
509+
*/
510+
public function setTotal($total)
511+
{
512+
$this->quantity = $total / $this->pricePerUnit;
513+
}
514+
}
515+
516+
.. code-block:: yaml
517+
518+
Invoice:
519+
total:
520+
getter: getTotal
521+
setter: setTotal
522+
523+
.. code-block:: xml
524+
525+
<?xml version="1.0" ?>
526+
527+
<property-access xmlns="http://symfony.com/schema/dic/property-access-mapping"
528+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
529+
xsi:schemaLocation="http://symfony.com/schema/dic/property-access-mapping http://symfony.com/schema/dic/property-access-mapping/property-access-mapping-1.0.xsd">
530+
531+
<class name="Invoice">
532+
<property name="total" getter="getTotal" setter="setTotal" />
533+
</class>
534+
535+
</property-access>
536+
537+
.. code-block:: php
538+
539+
$invoice = new Invoice();
540+
541+
$accessor->setValue($invoice, 'quantity', 20);
542+
$accessor->setValue($invoice, 'pricePerUnit', 10);
543+
var_dump('Total: '.$accesor->getValue($invoice, 'total'));
544+
// will return 'Total: 200'
545+
546+
Using property metadata with Symfony
547+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
548+
549+
By default, Symfony will look for property metadata in the following places
550+
inside each bundle path:
551+
552+
- `<Bundle path>/Resources/config/property_access.xml`
553+
- `<Bundle path>/Resources/config/property_access.yml`
554+
- `<Bundle path>/Resources/config/property_access/*.xml`
555+
- `<Bundle path>/Resources/config/property_access/*.yml`
556+
557+
If you need getting metadata from annotations you must explicitly enable them:
558+
559+
.. configuration-block::
560+
561+
.. code-block:: yaml
562+
563+
# app/config/config.yml
564+
framework:
565+
property_access: { enable_annotations: true }
566+
567+
.. code-block:: xml
568+
569+
<!-- app/config/config.xml -->
570+
<?xml version="1.0" encoding="UTF-8" ?>
571+
<container xmlns="http://symfony.com/schema/dic/services"
572+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
573+
xmlns:framework="http://symfony.com/schema/dic/symfony"
574+
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd
575+
http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
576+
577+
<framework:config>
578+
<framework:property_access enable-annotations="true" />
579+
</framework:config>
580+
</container>
581+
582+
.. code-block:: php
583+
584+
// app/config/config.php
585+
$container->loadFromExtension('framework', array(
586+
'property_access' => array(
587+
'enable_annotations' => true,
588+
),
589+
));
590+
368591
Enable other Features
369-
~~~~~~~~~~~~~~~~~~~~~
592+
---------------------
370593

371594
The :class:`Symfony\\Component\\PropertyAccess\\PropertyAccessor` can be
372595
configured to enable extra features. To do that you could use the
@@ -397,5 +620,55 @@ Or you can pass parameters directly to the constructor (not the recommended way)
397620
// ...
398621
$accessor = new PropertyAccessor(true); // this enables handling of magic __call
399622

623+
If you need to enable metadata processing (see
624+
`Custom method calls and virtual properties in a class`_) you must instantiate
625+
a :class:`Symfony\\Componente\\PropertyAcces\\Mapping\\Factory\\MetadataFactoryInterface`
626+
and use the method `setMetadataFactory` on the
627+
:class:`Symfony\\Component\\PropertyAccess\\PropertyAccessorBuilder`. Bundled with
628+
the component you can find
629+
a `MetadataFactory` class that supports different kind of loaders (annotations,
630+
YAML and YML files) called :class:`
631+
Symfony\\Componente\\PropertyAcces\\Mapping\\Factory\\LazyLoadingMetadataFactory`.
632+
633+
Its constructor needs a :class:`
634+
Symfony\\Component\\PropertyAccess\\Mapping\\Loader\\LoaderInterface` which specifies
635+
the source of the metadata information. You can also use a PSR6 compliant cache
636+
as the second parameter passing a :class:`Psr\\Cache\\CacheItemPoolInterface`
637+
reference.
638+
639+
.. code-block:: php
640+
641+
use Doctrine\Common\Annotations\AnnotationReader;
642+
use Symfony\Component\PropertyAccess\Mapping\Factory\LazyLoadingMetadataFactory;
643+
use Symfony\Component\PropertyAccess\Mapping\Loader\AnnotationLoader;
644+
use Symfony\Component\PropertyAccess\Mapping\Loader\LoaderChain;
645+
use Symfony\Component\PropertyAccess\Mapping\Loader\XMLFileLoader;
646+
use Symfony\Component\PropertyAccess\Mapping\Loader\YamlFileLoader;
647+
648+
// ...
649+
650+
$accessorBuilder = PropertyAccess::createPropertyAccessorBuilder();
651+
652+
// Create annotation loader using Doctrine annotation reader
653+
$loader = new AnnotationLoader(new AnnotationReader());
654+
655+
// or read metadata from a XML file
656+
$loader = new XmlFileLoader('metadata.xml');
657+
658+
// or read metadata from a YAML file
659+
$loader = new YamlFileLoader('metadata.yml');
660+
661+
// or combine several loaders in one
662+
$loader = new LoaderChain(
663+
new AnnotationLoader(new AnnotationReader()),
664+
new XmlFileLoader('metadata.xml'),
665+
new YamlFileLoader('metadata.yml'),
666+
new YamlFileLoader('metadata2.yml')
667+
);
668+
669+
// Enable metadata loading
670+
$metadataFactory = new LazyLoadingMetadataFactory($loader);
671+
672+
$accessorBuilder->setMetadataFactory($metadataFactory);
400673
401674
.. _Packagist: https://packagist.org/packages/symfony/property-access

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.