@@ -116,6 +116,9 @@ property name (``first_name`` becomes ``FirstName``) and prefixes it with
116
116
117
117
var_dump($accessor->getValue($person, 'first_name')); // 'Wouter'
118
118
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
+
119
122
Using Hassers/Issers
120
123
~~~~~~~~~~~~~~~~~~~~
121
124
@@ -314,6 +317,9 @@ see `Enable other Features`_.
314
317
315
318
var_dump($person->getWouter()); // array(...)
316
319
320
+ You can override the called setter method using metadata (i.e. annotations or
321
+ configuration files). see `Custom method calls and virtual properties in a class `_
322
+
317
323
Writing to Array Properties
318
324
~~~~~~~~~~~~~~~~~~~~~~~~~~~
319
325
@@ -418,8 +424,225 @@ You can also mix objects and arrays::
418
424
var_dump('Hello '.$accessor->getValue($person, 'children[0].firstName')); // 'Wouter'
419
425
// equal to $person->getChildren()[0]->firstName
420
426
427
+ Custom method calls and virtual properties in a class
428
+ -----------------------------------------------------
429
+
430
+ Sometimes you may not want the component to guess which method has to be called
431
+ when reading or writing properties. This is specially interesting when property
432
+ names are not in English or its singularization is not properly detected.
433
+
434
+ For those cases you can add metadata to the class being accessed so that the
435
+ component will use a particular method as a getter, setter or even adder and
436
+ remover (for collections).
437
+
438
+ Another interesting use of custom methods is declaring virtual properties
439
+ which are not stored directly in the object.
440
+
441
+ There are three supported ways to state this metadata supported out-of-the-box by
442
+ the component: using annotations, using YAML configuration files or using XML
443
+ configuration files.
444
+
445
+ .. caution ::
446
+
447
+ When using as a standalone component the metadata feature is disabled by
448
+ default. You can enable it by calling
449
+ :method: `PropertyAccessorBuilder::setMetadataFactory
450
+ <Symfony\\ Component\\ PropertyAccess\\ PropertyAccessorBuilder::setMetadataFactory> `
451
+ see `Enable other Features `_.
452
+
453
+ There are four method calls that can be overriden: `getter `, `setter `, `adder ` and
454
+ `remover `.
455
+
456
+ When using annotations you can precede a property with `@Property ` to state which
457
+ method should be called when a get, set, add or remove operation is needed on the
458
+ property.
459
+
460
+ .. configuration-block ::
461
+
462
+ .. code-block :: php
463
+
464
+ // ...
465
+ use Symfony\Component\PropertyAccess\Annotation\Property;
466
+
467
+ class Person
468
+ {
469
+ /**
470
+ * @Property(getter="getFullName", setter="setFullName")
471
+ */
472
+ private $name;
473
+
474
+ /**
475
+ * @Property(adder="addNewChild", remover="discardChild")
476
+ */
477
+ private $children;
478
+
479
+ public function getFullName()
480
+ {
481
+ return $this->name;
482
+ }
483
+
484
+ public function setFullName($fullName)
485
+ {
486
+ $this->name = $fullName;
487
+ }
488
+ }
489
+
490
+ .. code-block :: yaml
491
+
492
+ Person :
493
+ name :
494
+ getter : getFullName
495
+ setter : setFullName
496
+ children :
497
+ adder : addNewChild
498
+ remover : discardChild
499
+
500
+ .. code-block :: xml
501
+
502
+ <?xml version =" 1.0" ?>
503
+
504
+ <property-access xmlns =" http://symfony.com/schema/dic/property-access-mapping"
505
+ xmlns : xsi =" http://www.w3.org/2001/XMLSchema-instance"
506
+ xsi : schemaLocation =" http://symfony.com/schema/dic/property-access-mapping http://symfony.com/schema/dic/property-access-mapping/property-access-mapping-1.0.xsd" >
507
+
508
+ <class name =" Person" >
509
+ <property name =" name" getter =" getFullName" setter =" setFullName" />
510
+ <property name =" children" adder =" addNewChild" remover =" discardChild" />
511
+ </class >
512
+
513
+ </property-access >
514
+
515
+ Then, using the overriden methods is automatic:
516
+
517
+ .. code-block :: php
518
+
519
+ $person = new Person();
520
+
521
+ $accessor->setValue($person, 'name', 'John Doe');
522
+ // will call setFullName
523
+
524
+ var_dump('Hello '.$accesor->getValue($person, 'name'));
525
+ // will return 'Hello John Doe'
526
+
527
+ You can also associate a particular method with an operation on a property
528
+ using the `@Getter `, `@Setter `, `@Adder ` and `@Remover ` annotations. All of them
529
+ take only one parameter: `property `.
530
+
531
+ This allows creating virtual properties that are not directly stored in the
532
+ object::
533
+
534
+ .. configuration-block ::
535
+
536
+ .. code-block :: php
537
+
538
+ // ...
539
+ use Symfony\Component\PropertyAccess\Annotation\Getter;
540
+ use Symfony\Component\PropertyAccess\Annotation\Setter;
541
+
542
+ class Invoice
543
+ {
544
+ private $quantity;
545
+
546
+ private $pricePerUnit;
547
+
548
+ // Notice that there is no real "total" property
549
+
550
+ /**
551
+ * @Getter(property="total")
552
+ */
553
+ public function getTotal()
554
+ {
555
+ return $this->quantity * $this->pricePerUnit;
556
+ }
557
+
558
+ /**
559
+ * @Setter(property="total")
560
+ *
561
+ * @param mixed $total
562
+ */
563
+ public function setTotal($total)
564
+ {
565
+ $this->quantity = $total / $this->pricePerUnit;
566
+ }
567
+ }
568
+
569
+ .. code-block :: yaml
570
+
571
+ Invoice :
572
+ total :
573
+ getter : getTotal
574
+ setter : setTotal
575
+
576
+ .. code-block :: xml
577
+
578
+ <?xml version =" 1.0" ?>
579
+
580
+ <property-access xmlns =" http://symfony.com/schema/dic/property-access-mapping"
581
+ xmlns : xsi =" http://www.w3.org/2001/XMLSchema-instance"
582
+ xsi : schemaLocation =" http://symfony.com/schema/dic/property-access-mapping http://symfony.com/schema/dic/property-access-mapping/property-access-mapping-1.0.xsd" >
583
+
584
+ <class name =" Invoice" >
585
+ <property name =" total" getter =" getTotal" setter =" setTotal" />
586
+ </class >
587
+
588
+ </property-access >
589
+
590
+ .. code-block :: php
591
+
592
+ $invoice = new Invoice();
593
+
594
+ $accessor->setValue($invoice, 'quantity', 20);
595
+ $accessor->setValue($invoice, 'pricePerUnit', 10);
596
+ var_dump('Total: '.$accesor->getValue($invoice, 'total'));
597
+ // will return 'Total: 200'
598
+
599
+ Using property metadata with Symfony
600
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
601
+
602
+ By default, Symfony will look for property metadata in the following places
603
+ inside each bundle path:
604
+
605
+ - `<Bundle path>/Resources/config/property_accessor.xml `
606
+ - `<Bundle path>/Resources/config/property_accessor.yml `
607
+ - `<Bundle path>/Resources/config/property_accessor/*.xml `
608
+ - `<Bundle path>/Resources/config/property_accessor/*.yml `
609
+
610
+ If you need getting metadata from annotations you must explicitly enable them:
611
+
612
+ .. configuration-block ::
613
+
614
+ .. code-block :: yaml
615
+
616
+ # app/config/config.yml
617
+ framework :
618
+ property_access : { enable_annotations: true }
619
+
620
+ .. code-block :: xml
621
+
622
+ <!-- app/config/config.xml -->
623
+ <?xml version =" 1.0" encoding =" UTF-8" ?>
624
+ <container xmlns =" http://symfony.com/schema/dic/services"
625
+ xmlns : xsi =" http://www.w3.org/2001/XMLSchema-instance"
626
+ xmlns : framework =" http://symfony.com/schema/dic/symfony"
627
+ xsi : schemaLocation =" http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd
628
+ http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd" >
629
+
630
+ <framework : config >
631
+ <framework : property_access enable-annotations =" true" />
632
+ </framework : config >
633
+ </container >
634
+
635
+ .. code-block :: php
636
+
637
+ // app/config/config.php
638
+ $container->loadFromExtension('framework', array(
639
+ 'property_access' => array(
640
+ 'enable_annotations' => true,
641
+ ),
642
+ ));
643
+
421
644
Enable other Features
422
- ~~~~~~~~~~~~~~~~~~~~~
645
+ ---------------------
423
646
424
647
The :class: `Symfony\\ Component\\ PropertyAccess\\ PropertyAccessor ` can be
425
648
configured to enable extra features. To do that you could use the
@@ -450,6 +673,56 @@ Or you can pass parameters directly to the constructor (not the recommended way)
450
673
// ...
451
674
$accessor = new PropertyAccessor(true); // this enables handling of magic __call
452
675
676
+ If you need to enable metadata processing (see
677
+ `Custom method calls and virtual properties in a class `_) you must instantiate
678
+ a :class: `Symfony\\ Componente\\ PropertyAcces\\ Mapping\\ Factory\\ MetadataFactoryInterface `
679
+ and use the method `setMetadataFactory ` on the
680
+ :class: `Symfony\\ Component\\ PropertyAccess\\ PropertyAccessorBuilder `. Bundled with
681
+ the component you can find
682
+ a `MetadataFactory ` class that supports different kind of loaders (annotations,
683
+ YAML and YML files) called :class: `
684
+ Symfony\\ Componente\\ PropertyAcces\\ Mapping\\ Factory\\ LazyLoadingMetadataFactory `.
685
+
686
+ Its constructor needs a :class: `
687
+ Symfony\\ Component\\ PropertyAccess\\ Mapping\\ Loader\\ LoaderInterface ` which specifies
688
+ the source of the metadata information. You can also use a PSR6 compliant cache
689
+ as the second parameter passing a :class: `Psr\\ Cache\\ CacheItemPoolInterface `
690
+ reference.
691
+
692
+ .. code-block :: php
693
+
694
+ use Doctrine\Common\Annotations\AnnotationReader;
695
+ use Symfony\Component\PropertyAccess\Mapping\Factory\LazyLoadingMetadataFactory;
696
+ use Symfony\Component\PropertyAccess\Mapping\Loader\AnnotationLoader;
697
+ use Symfony\Component\PropertyAccess\Mapping\Loader\LoaderChain;
698
+ use Symfony\Component\PropertyAccess\Mapping\Loader\XMLFileLoader;
699
+ use Symfony\Component\PropertyAccess\Mapping\Loader\YamlFileLoader;
700
+
701
+ // ...
702
+
703
+ $accessorBuilder = PropertyAccess::createPropertyAccessorBuilder();
704
+
705
+ // Create annotation loader using Doctrine annotation reader
706
+ $loader = new AnnotationLoader(new AnnotationReader());
707
+
708
+ // or read metadata from a XML file
709
+ $loader = new XmlFileLoader('metadata.xml');
710
+
711
+ // or read metadata from a YAML file
712
+ $loader = new YamlFileLoader('metadata.yml');
713
+
714
+ // or combine several loaders in one
715
+ $loader = new LoaderChain(
716
+ new AnnotationLoader(new AnnotationReader()),
717
+ new XmlFileLoader('metadata.xml'),
718
+ new YamlFileLoader('metadata.yml'),
719
+ new YamlFileLoader('metadata2.yml')
720
+ );
721
+
722
+ // Enable metadata loading
723
+ $metadataFactory = new LazyLoadingMetadataFactory($loader);
724
+
725
+ $accessorBuilder->setMetadataFactory($metadataFactory);
453
726
454
727
.. _Packagist : https://packagist.org/packages/symfony/property-access
455
728
.. _The Inflector component : https://github.com/symfony/inflector
0 commit comments