@@ -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
@@ -306,6 +309,9 @@ see `Enable other Features`_.
306
309
307
310
var_dump($person->getWouter()); // array(...)
308
311
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
+
309
315
Checking Property Paths
310
316
-----------------------
311
317
@@ -365,8 +371,224 @@ You can also mix objects and arrays::
365
371
var_dump('Hello '.$accessor->getValue($person, 'children[0].firstName')); // 'Wouter'
366
372
// equal to $person->getChildren()[0]->firstName
367
373
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 remover (for collections).
383
+
384
+ Another interesting use of custom methods is declaring virtual properties
385
+ which are not stored directly in the object.
386
+
387
+ There are three supported ways to state this metadata supported out-of-the-box by
388
+ the component: using annotations, using YAML configuration files or using XML
389
+ configuration files.
390
+
391
+ .. caution ::
392
+
393
+ When using the component as standalone the metadata feature is disabled by
394
+ default. You can enable it by calling
395
+ :method: `PropertyAccessorBuilder::setMetadataFactory
396
+ <Symfony\\ Component\\ PropertyAccess\\ PropertyAccessorBuilder::setMetadataFactory> `
397
+ see `Enable other Features `_.
398
+
399
+ There are four method calls that can be overriden: `getter `, `setter `, `adder ` and
400
+ `remover `.
401
+
402
+ When using annotations you can precede a property with `@Property ` to state which
403
+ method should be called when a get, set, add or remove operation is needed on the
404
+ property.
405
+
406
+ .. configuration-block ::
407
+
408
+ .. code-block :: php
409
+
410
+ // ...
411
+ use Symfony\Component\PropertyAccess\Annotation\Property;
412
+
413
+ class Person
414
+ {
415
+ /**
416
+ * @Property(getter="getFullName", setter="setFullName")
417
+ */
418
+ private $name;
419
+
420
+ /**
421
+ * @Property(adder="addNewChild", remover="discardChild")
422
+ */
423
+ private $children;
424
+
425
+ public function getFullName()
426
+ {
427
+ return $this->name;
428
+ }
429
+
430
+ public function setFullName($fullName)
431
+ {
432
+ $this->name = $fullName;
433
+ }
434
+ }
435
+
436
+ .. code-block :: yaml
437
+
438
+ Person :
439
+ name :
440
+ getter : getFullName
441
+ setter : setFullName
442
+ children :
443
+ adder : addNewChild
444
+ remover : discardChild
445
+
446
+ .. code-block :: xml
447
+
448
+ <?xml version =" 1.0" ?>
449
+
450
+ <property-access xmlns =" http://symfony.com/schema/dic/property-access-mapping"
451
+ xmlns : xsi =" http://www.w3.org/2001/XMLSchema-instance"
452
+ xsi : schemaLocation =" http://symfony.com/schema/dic/property-access-mapping http://symfony.com/schema/dic/property-access-mapping/property-access-mapping-1.0.xsd" >
453
+
454
+ <class name =" Person" >
455
+ <property name =" name" getter =" getFullName" setter =" setFullName" />
456
+ <property name =" children" adder =" addNewChild" remover =" discardChild" />
457
+ </class >
458
+
459
+ </property-access >
460
+
461
+ Then, using the overriden methods is automatic:
462
+
463
+ .. code-block :: php
464
+
465
+ $person = new Person();
466
+
467
+ $accessor->setValue($person, 'name', 'John Doe');
468
+ // will call setFullName
469
+
470
+ var_dump('Hello '.$accesor->getValue($person, 'name'));
471
+ // will return 'Hello John Doe'
472
+
473
+ You can also associate a particular method with an operation on a property
474
+ using the `@Getter `, `@Setter `, `@Adder ` and `@Remover ` annotations. All of them
475
+ take only one parameter: `property `.
476
+
477
+ This allows creating virtual properties that are not directly stored in the
478
+ object::
479
+
480
+ .. configuration-block ::
481
+
482
+ .. code-block :: php
483
+
484
+ // ...
485
+ use Symfony\Component\PropertyAccess\Annotation\Getter;
486
+ use Symfony\Component\PropertyAccess\Annotation\Setter;
487
+
488
+ class Invoice
489
+ {
490
+ private $quantity;
491
+
492
+ private $pricePerUnit;
493
+
494
+ // Notice that there is no real "total" property
495
+
496
+ /**
497
+ * @Getter(property="total")
498
+ */
499
+ public function getTotal()
500
+ {
501
+ return $this->quantity * $this->pricePerUnit;
502
+ }
503
+
504
+ /**
505
+ * @Setter(property="total")
506
+ *
507
+ * @param mixed $total
508
+ */
509
+ public function setTotal($total)
510
+ {
511
+ $this->quantity = $total / $this->pricePerUnit;
512
+ }
513
+ }
514
+
515
+ .. code-block :: yaml
516
+
517
+ Invoice :
518
+ total :
519
+ getter : getTotal
520
+ setter : setTotal
521
+
522
+ .. code-block :: xml
523
+
524
+ <?xml version =" 1.0" ?>
525
+
526
+ <property-access xmlns =" http://symfony.com/schema/dic/property-access-mapping"
527
+ xmlns : xsi =" http://www.w3.org/2001/XMLSchema-instance"
528
+ xsi : schemaLocation =" http://symfony.com/schema/dic/property-access-mapping http://symfony.com/schema/dic/property-access-mapping/property-access-mapping-1.0.xsd" >
529
+
530
+ <class name =" Invoice" >
531
+ <property name =" total" getter =" getTotal" setter =" setTotal" />
532
+ </class >
533
+
534
+ </property-access >
535
+
536
+ .. code-block :: php
537
+
538
+ $invoice = new Invoice();
539
+
540
+ $accessor->setValue($invoice, 'quantity', 20);
541
+ $accessor->setValue($invoice, 'pricePerUnit', 10);
542
+ var_dump('Total: '.$accesor->getValue($invoice, 'total'));
543
+ // will return 'Total: 200'
544
+
545
+ Using property metadata with Symfony
546
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
547
+
548
+ By default, Symfony will look for property metadata in the following places
549
+ inside each bundle path:
550
+
551
+ - `<Bundle path>/Resources/config/property_access.xml `
552
+ - `<Bundle path>/Resources/config/property_access.yml `
553
+ - `<Bundle path>/Resources/config/property_access/*.xml `
554
+ - `<Bundle path>/Resources/config/property_access/*.yml `
555
+
556
+ If you need getting metadata from annotations you must explicitly enable them:
557
+
558
+ .. configuration-block ::
559
+
560
+ .. code-block :: yaml
561
+
562
+ # app/config/config.yml
563
+ framework :
564
+ property_access : { enable_annotations: true }
565
+
566
+ .. code-block :: xml
567
+
568
+ <!-- app/config/config.xml -->
569
+ <?xml version =" 1.0" encoding =" UTF-8" ?>
570
+ <container xmlns =" http://symfony.com/schema/dic/services"
571
+ xmlns : xsi =" http://www.w3.org/2001/XMLSchema-instance"
572
+ xmlns : framework =" http://symfony.com/schema/dic/symfony"
573
+ xsi : schemaLocation =" http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd
574
+ http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd" >
575
+
576
+ <framework : config >
577
+ <framework : property_access enable-annotations =" true" />
578
+ </framework : config >
579
+ </container >
580
+
581
+ .. code-block :: php
582
+
583
+ // app/config/config.php
584
+ $container->loadFromExtension('framework', array(
585
+ 'property_access' => array(
586
+ 'enable_annotations' => true,
587
+ ),
588
+ ));
589
+
368
590
Enable other Features
369
- ~~~~~~~~~~~~~~~~~~~~~
591
+ ---------------------
370
592
371
593
The :class: `Symfony\\ Component\\ PropertyAccess\\ PropertyAccessor ` can be
372
594
configured to enable extra features. To do that you could use the
@@ -397,5 +619,55 @@ Or you can pass parameters directly to the constructor (not the recommended way)
397
619
// ...
398
620
$accessor = new PropertyAccessor(true); // this enables handling of magic __call
399
621
622
+ If you need to enable metadata processing (see
623
+ `Custom method calls and virtual properties in a class `_) you must instantiate
624
+ a :class: `Symfony\\ Componente\\ PropertyAcces\\ Mapping\\ Factory\\ MetadataFactoryInterface `
625
+ and use the method `setMetadataFactory ` on the
626
+ :class: `Symfony\\ Component\\ PropertyAccess\\ PropertyAccessorBuilder `. Bundled with
627
+ the component you can find
628
+ a `MetadataFactory ` class that supports different kind of loaders (annotations,
629
+ YAML and YML files) called :class: `
630
+ Symfony\\ Componente\\ PropertyAcces\\ Mapping\\ Factory\\ LazyLoadingMetadataFactory `.
631
+
632
+ Its constructor needs a :class: `
633
+ Symfony\\ Component\\ PropertyAccess\\ Mapping\\ Loader\\ LoaderInterface ` which specifies
634
+ the source of the metadata information. You can also use a PSR6 compliant cache
635
+ as the second parameter passing a :class: `Psr\\ Cache\\ CacheItemPoolInterface `
636
+ reference.
637
+
638
+ .. code-block :: php
639
+
640
+ use Doctrine\Common\Annotations\AnnotationReader;
641
+ use Symfony\Component\PropertyAccess\Mapping\Factory\LazyLoadingMetadataFactory;
642
+ use Symfony\Component\PropertyAccess\Mapping\Loader\AnnotationLoader;
643
+ use Symfony\Component\PropertyAccess\Mapping\Loader\LoaderChain;
644
+ use Symfony\Component\PropertyAccess\Mapping\Loader\XMLFileLoader;
645
+ use Symfony\Component\PropertyAccess\Mapping\Loader\YamlFileLoader;
646
+
647
+ // ...
648
+
649
+ $accessorBuilder = PropertyAccess::createPropertyAccessorBuilder();
650
+
651
+ // Create annotation loader using Doctrine annotation reader
652
+ $loader = new AnnotationLoader(new AnnotationReader());
653
+
654
+ // or read metadata from a XML file
655
+ $loader = new XmlFileLoader('metadata.xml');
656
+
657
+ // or read metadata from a YAML file
658
+ $loader = new YamlFileLoader('metadata.yml');
659
+
660
+ // or combine several loaders in one
661
+ $loader = new LoaderChain(
662
+ new AnnotationLoader(new AnnotationReader()),
663
+ new XmlFileLoader('metadata.xml'),
664
+ new YamlFileLoader('metadata.yml'),
665
+ new YamlFileLoader('metadata2.yml')
666
+ );
667
+
668
+ // Enable metadata loading
669
+ $metadataFactory = new LazyLoadingMetadataFactory($loader);
670
+
671
+ $accessorBuilder->setMetadataFactory($metadataFactory);
400
672
401
673
.. _Packagist : https://packagist.org/packages/symfony/property-access
0 commit comments