@@ -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,225 @@ 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
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
+
368
591
Enable other Features
369
- ~~~~~~~~~~~~~~~~~~~~~
592
+ ---------------------
370
593
371
594
The :class: `Symfony\\ Component\\ PropertyAccess\\ PropertyAccessor ` can be
372
595
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)
397
620
// ...
398
621
$accessor = new PropertyAccessor(true); // this enables handling of magic __call
399
622
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);
400
673
401
674
.. _Packagist : https://packagist.org/packages/symfony/property-access
0 commit comments