@@ -75,6 +75,11 @@ component accessors="true" transientCache="false" {
7575 property name = " _asMemento" default = " false" ;
7676 property name = " _asMementoSettings" ;
7777
78+ /**
79+ * Callbacks applied to each hydrated entity before return transformations.
80+ */
81+ property name = " _entityTransformers" ;
82+
7883 /**
7984 * Used to quickly identify QueryBuilder instances
8085 * instead of resorting to `isInstanceOf` which is slow.
@@ -94,6 +99,7 @@ component accessors="true" transientCache="false" {
9499 variables ._asMemento = false ;
95100 variables ._asQuery = false ;
96101 variables ._withAliases = false ;
102+ variables ._entityTransformers = [];
97103 param variables ._preventLazyLoading = false ;
98104 if ( ! variables .keyExists ( " _lazyLoadingViolationCallback" ) || isNull ( variables ._lazyLoadingViolationCallback ) ) {
99105 variables ._lazyLoadingViolationCallback = ( entity , relationName ) = > {
@@ -109,6 +115,18 @@ component accessors="true" transientCache="false" {
109115 return this ;
110116 }
111117
118+ /**
119+ * Adds a callback that transforms each hydrated entity before it is returned.
120+ *
121+ * @transformer The callback accepting and returning an entity.
122+ *
123+ * @return quick.models.QuickBuilder
124+ */
125+ public QuickBuilder function addEntityTransformer ( required any transformer ) {
126+ variables ._entityTransformers .append ( arguments .transformer );
127+ return this ;
128+ }
129+
112130 function onDIComplete () {
113131 variables .qb .setQuickBuilder ( this );
114132 variables .qb .setColumnFormatter ( function ( column ) {
@@ -1489,6 +1507,16 @@ component accessors="true" transientCache="false" {
14891507 * @return any
14901508 */
14911509 private any function handleTransformations ( entity ) {
1510+ if ( ! variables ._asQuery ) {
1511+ if ( isArray ( arguments .entity ) ) {
1512+ arguments .entity = arguments .entity .map ( function ( item ) {
1513+ return applyEntityTransformers ( arguments .item );
1514+ } );
1515+ } else if ( ! isNull ( arguments .entity ) ) {
1516+ arguments .entity = applyEntityTransformers ( arguments .entity );
1517+ }
1518+ }
1519+
14921520 if ( ! variables ._asMemento ) {
14931521 return arguments .entity ;
14941522 }
@@ -1502,6 +1530,15 @@ component accessors="true" transientCache="false" {
15021530 } );
15031531 }
15041532
1533+ /**
1534+ * Applies all configured entity transformers to one hydrated entity.
1535+ */
1536+ private any function applyEntityTransformers ( required any entity ) {
1537+ return variables ._entityTransformers .reduce ( function ( transformed , transformer ) {
1538+ return arguments .transformer ( arguments .transformed );
1539+ }, arguments .entity );
1540+ }
1541+
15051542 /**
15061543 * Activates the global scopes while checking for excluded global scopes.
15071544 *
@@ -1741,6 +1778,7 @@ component accessors="true" transientCache="false" {
17411778 newBuilder .set_withAliases ( this .get_withAliases () );
17421779 newBuilder .set_asMemento ( this .get_asMemento () );
17431780 newBuilder .set_asMementoSettings ( this .get_asMementoSettings () );
1781+ newBuilder .set_entityTransformers ( this .get_entityTransformers () );
17441782 return newBuilder ;
17451783 }
17461784
0 commit comments