<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Adam Anderly</title>
	<atom:link href="https://anderly.com/feed/" rel="self" type="application/rss+xml" />
	<link>https://anderly.com</link>
	<description>Husband, Father, Developer</description>
	<lastBuildDate>Thu, 01 Oct 2026 18:40:13 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0.5</generator>
	<item>
		<title>Publisher is gone. Here&#8217;s why I built PubKeep.</title>
		<link>https://anderly.com/2026/10/01/why-i-built-pubkeep/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=why-i-built-pubkeep</link>
					<comments>https://anderly.com/2026/10/01/why-i-built-pubkeep/#respond</comments>
		
		<dc:creator><![CDATA[Adam]]></dc:creator>
		<pubDate>Thu, 01 Oct 2026 18:40:09 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">https://anderly.com/?p=613</guid>

					<description><![CDATA[<p>Microsoft pulled Publisher from Microsoft 365 on October 1. My mom called first. This is why I built PubKeep, the desktop app that keeps your .pub files editable.</p>
<p>The post <a href="https://anderly.com/2026/10/01/why-i-built-pubkeep/">Publisher is gone. Here’s why I built PubKeep.</a> first appeared on <a href="https://anderly.com">Adam Anderly</a>.</p>]]></description>
										<content:encoded><![CDATA[<p class="wp-block-paragraph">It started with a phone call from my mom.</p>



<p class="wp-block-paragraph">Microsoft had announced it was removing Publisher from Microsoft 365, and she, my aunt, and my cousin all had years of files made in it. Newsletters, wedding programs, labels, the family photo calendar we got every Christmas. She wanted to know if I could help them get their files out before the app went away.</p>



<p class="wp-block-paragraph">I had not used Publisher in years. But I knew exactly what she meant, because I had used it for our own wedding program and thank-you cards. Publisher was the tool ordinary people used to make something that came out looking finished. And it was a tool you bought once.</p>



<p class="wp-block-paragraph">The more I looked, the bigger the problem got. Church offices build the weekly bulletin from last week&#8217;s .pub file. Schools have years of newsletters and certificates. Nonprofits, small businesses, the volunteer who makes the monthly newsletter. The suggested replacements are good tools, but they are subscriptions, and none of them open the files people already have. Everyone was being told to start over.</p>



<p class="wp-block-paragraph">So I started building.</p>



<h2 class="wp-block-heading">First, save the files</h2>



<p class="wp-block-paragraph">The urgent problem was the archive. If Publisher was going away, people needed a way to turn years of .pub files into something they could always open.</p>



<p class="wp-block-paragraph">The first thing I built was a converter: drop a whole folder of .pub files and get exact PDFs back, page for page. My family tried it the first day. My mom&#8217;s reply was &#8220;Oh hallelujah!&#8221;</p>



<p class="wp-block-paragraph">That saved the past. It didn&#8217;t help with next week&#8217;s bulletin.</p>



<h2 class="wp-block-heading">Then, keep editing</h2>



<p class="wp-block-paragraph">A PDF only saves what you&#8217;ve already made. The real question was why Publisher users should have to stop publishing at all.</p>



<p class="wp-block-paragraph">So I built PubKeep, a desktop app for Windows and Mac that opens your .pub files and keeps them editable. The same text boxes, master pages, and fold-over booklets you already use, without Publisher on the computer. No converting first, and nothing to rebuild.</p>



<p class="wp-block-paragraph">It&#8217;s a one-time purchase, not a subscription. You buy it once and it keeps working. And it saves to .pubk, an open format you can always get your work out of. After watching people lose access to their own work, I wasn&#8217;t going to build another lock-in.</p>



<h2 class="wp-block-heading">The people who helped build it</h2>



<p class="wp-block-paragraph">Before launch, more than 1,100 organizations signed up for early access, most of them churches. They opened their own bulletins and newsletters, told us what was missing, and sent the files that didn&#8217;t come through right so we could fix them.</p>



<p class="wp-block-paragraph">Some of what they wrote back has stuck with me. One church secretary said she got more done in 45 minutes than she had in other programs all week. An author told us she had thousands of Publisher files from 30 years of work, and that PubKeep was better than she had dared hope for.</p>



<h2 class="wp-block-heading">Launch</h2>



<p class="wp-block-paragraph">PubKeep went on sale September 30, the day before Microsoft pulled Publisher from Microsoft 365, with a free 7-day trial.</p>



<h2 class="wp-block-heading">What&#8217;s next</h2>



<p class="wp-block-paragraph">Support for the perpetual versions of Publisher ends October 13, so the next few weeks are about helping everyone who still depends on it. We keep widening what PubKeep opens, and the files people send us decide what we work on next.</p>



<p class="wp-block-paragraph">If you or someone you know has a pile of .pub files, PubKeep is at <a href="https://pubkeep.com">pubkeep.com</a>, and the free trial needs no card.</p>



<p class="wp-block-paragraph">And if your mom calls about her Publisher files, now you know what to tell her.</p><p>The post <a href="https://anderly.com/2026/10/01/why-i-built-pubkeep/">Publisher is gone. Here’s why I built PubKeep.</a> first appeared on <a href="https://anderly.com">Adam Anderly</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://anderly.com/2026/10/01/why-i-built-pubkeep/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Cross-Cutting Concerns with MediatR Pipeline Behaviors</title>
		<link>https://anderly.com/2019/12/12/cross-cutting-concerns-with-mediatr-pipeline-behaviors/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=cross-cutting-concerns-with-mediatr-pipeline-behaviors</link>
					<comments>https://anderly.com/2019/12/12/cross-cutting-concerns-with-mediatr-pipeline-behaviors/#respond</comments>
		
		<dc:creator><![CDATA[Adam]]></dc:creator>
		<pubDate>Thu, 12 Dec 2019 16:41:14 +0000</pubDate>
				<category><![CDATA[.NET]]></category>
		<category><![CDATA[ASP.NET Core]]></category>
		<category><![CDATA[MediatR]]></category>
		<category><![CDATA[Caching]]></category>
		<category><![CDATA[Fallback]]></category>
		<category><![CDATA[Polly]]></category>
		<category><![CDATA[Retry]]></category>
		<category><![CDATA[Scrutor]]></category>
		<guid isPermaLink="false">http://anderly.com/?p=541</guid>

					<description><![CDATA[<p>I&#8217;ve recently been exploring MediatR Pipeline Behaviors and cross-cutting concerns (logging, validation, security, transactions) but also wanted to tackle some performance and resilience scenarios that may not always apply to every request/command (retry, fallback and caching). Here are some gists that tackle these concerns: Caching Behavior (using ASP.NET Core IDistributedCache): For caching, I&#8217;m using a [&#8230;]</p>
<p>The post <a href="https://anderly.com/2019/12/12/cross-cutting-concerns-with-mediatr-pipeline-behaviors/">Cross-Cutting Concerns with MediatR Pipeline Behaviors</a> first appeared on <a href="https://anderly.com">Adam Anderly</a>.</p>]]></description>
										<content:encoded><![CDATA[<p class="wp-block-paragraph">I&#8217;ve recently been exploring <a href="https://github.com/jbogard/MediatR">MediatR</a> Pipeline <a href="https://github.com/jbogard/MediatR/wiki/Behaviors">Behaviors</a> and cross-cutting concerns (logging, validation, security, transactions) but also wanted to tackle some performance and resilience scenarios that may not always apply to every request/command (retry, fallback and caching).</p>



<p class="wp-block-paragraph">Here are some gists that tackle these concerns:</p>



<p class="wp-block-paragraph"><strong>Caching Behavior (using ASP.NET Core <a href="https://docs.microsoft.com/en-us/aspnet/core/performance/caching/distributed?view=aspnetcore-3.1">IDistributedCache</a>):</strong></p>



<p class="wp-block-paragraph">For caching, I&#8217;m using a separately defined CachePolicy similar to a <a href="https://fluentvalidation.net/">FluentValidation</a> Validator class.</p>


<p><script src="https://gist.github.com/anderly/9ccab0c57c593975f32cce3ad6430652.js"></script></p>


<p class="wp-block-paragraph"><strong>Fallback Behavior (using <a href="https://github.com/App-vNext/Polly">Polly</a>):</strong></p>



<p class="wp-block-paragraph">For Fallback and Retry behavior, I&#8217;m using separate interfaces  IFallbackHandler and IRetryableRequest to get the behavior.</p>


<p><script src="https://gist.github.com/anderly/576aece4c7c37ce8a6cc1fc5a7f6bb9e.js"></script></p>


<p class="wp-block-paragraph"><strong>Retry Behavior (using <a href="https://github.com/App-vNext/Polly">Polly</a>):</strong></p>


<p><script src="https://gist.github.com/anderly/49f34162605d639a2a0c265bb5a0d00a.js"></script></p>


<p class="wp-block-paragraph">These can all be combined as well and work based on the order they&#8217;re registered in Startup.cs. In my case, I want my main <strong>Handle </strong>method to retry, then, optionally, fallback to my <strong>HandleFallback</strong>, then finally cache the result based on the defined CachePolicy. </p>



<p class="wp-block-paragraph">I&#8217;m debating between using separately defined classes/policies (like <a href="https://fluentvalidation.net/">FluentValidation</a> Validators) vs. making Handlers implement the interfaces to get the behavior. On one hand, if they&#8217;re separate, I could move the Cache, Fallback and Retry policies to separate files, folders, etc. and keep my main MediatR handlers very lean and clean. If you want to see them in the same place, you could put them in the same file like many of Jimmy Bogard&#8217;s <a href="https://github.com/jbogard/ContosoUniversityDotNetCore-Pages">examples</a> with <a href="https://github.com/jbogard/ContosoUniversityDotNetCore-Pages/blob/master/ContosoUniversity/Pages/Courses/Edit.cshtml.cs#L80">Validators</a>, etc. But if the files get too big and you want to separate them out, you could do that. However, with Retry and Fallback, it&#8217;s kind of nice seeing that with the main request handler.</p>



<p class="wp-block-paragraph">Of course, all this gets wired-up and registered in <strong>Startup.cs</strong>. I&#8217;m using <a href="https://github.com/khellang/Scrutor">Scrutor</a> to aid in discovery my Caching, Fallback and Retry policies. See below.</p>


<p><script src="https://gist.github.com/anderly/53e3b98c440479a9171643a79d2720d7.js"></script></p>


<p class="wp-block-paragraph">What do you think? Which approach do you prefer? Let me know in the comments.</p><p>The post <a href="https://anderly.com/2019/12/12/cross-cutting-concerns-with-mediatr-pipeline-behaviors/">Cross-Cutting Concerns with MediatR Pipeline Behaviors</a> first appeared on <a href="https://anderly.com">Adam Anderly</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://anderly.com/2019/12/12/cross-cutting-concerns-with-mediatr-pipeline-behaviors/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Laravel User Timezone Aware Trait</title>
		<link>https://anderly.com/2017/12/21/laravel-user-timezone-aware-trait/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=laravel-user-timezone-aware-trait</link>
					<comments>https://anderly.com/2017/12/21/laravel-user-timezone-aware-trait/#respond</comments>
		
		<dc:creator><![CDATA[Adam]]></dc:creator>
		<pubDate>Thu, 21 Dec 2017 17:36:21 +0000</pubDate>
				<category><![CDATA[Laravel]]></category>
		<category><![CDATA[Laravel Traits]]></category>
		<category><![CDATA[PHP]]></category>
		<guid isPermaLink="false">http://anderly.com/?p=521</guid>

					<description><![CDATA[<p>Next up in the series on useful Laravel model traits is the UserTimezoneAware Trait. This trait is useful when you have dates on models that you need to display in the user&#8217;s timezone. &#60;pre class=&#34;lang:php decode:true&#34;&#62;// app\Traits\UserTimezoneAware.php namespace App\Traits; trait UserTimezoneAware { /** * The attribute name containing the timezone (defaults to &#34;timezone&#34;). * * [&#8230;]</p>
<p>The post <a href="https://anderly.com/2017/12/21/laravel-user-timezone-aware-trait/">Laravel User Timezone Aware Trait</a> first appeared on <a href="https://anderly.com">Adam Anderly</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>Next up in the series on <a href="http://anderly.com/category/laravel-traits/" target="_blank" rel="noopener noreferrer">useful Laravel model traits</a> is the <code data-enlighter-language="generic" class="EnlighterJSRAW">UserTimezoneAware</code> Trait.</p>
<p>This trait is useful when you have dates on models that you need to display in the user&#8217;s timezone.</p>
<pre data-enlighter-language="php" class="EnlighterJSRAW">
&lt;pre class=&quot;lang:php decode:true&quot;&gt;// app\Traits\UserTimezoneAware.php
namespace App\Traits;
trait UserTimezoneAware
{
    /**
     * The attribute name containing the timezone (defaults to &quot;timezone&quot;).
     *
     * @var string
     */
    public $timezoneAttribute = &#039;timezone&#039;;
     
    /**
     * Return the passed date in the user&#039;s timezone (or default to the app timezone)
     *
     * @return string
    */
    public function getDateToUserTimezone($date, $timezone = null)
    {
        if ($timezone == null) {
            if (Auth::check()) {
                $timezone = Auth::user()-&gt;timezone;
            } else {
                $timezone = Config::get(&#039;app.timezone&#039;);
            }
        }
        $datetime = new DateTime($date);
        $datetime-&gt;setTimezone(new datetimezone($timezone));
        
        return $datetime-&gt;format(&#039;c&#039;);
    }   
}

&lt;/pre&gt;
</pre>
<p>It&#8217;s a simple trait you can drop onto one of your eloquent models to add timezone support like so:</p>
<pre data-enlighter-language="php" class="EnlighterJSRAW">
&lt;pre class=&quot;lang:php decode:true&quot;&gt;// app\Models\User.php
namespace App\Models;

use App\Traits\UserTimezoneAware

class User
{
    use UserTimezoneAware;

    public function getLastLoginAttribute()
    {
        return $this-&gt;getDateToUserTimezone($this-&gt;attributes[&#039;last_login&#039;]);
    }
}
&lt;/pre&gt;
</pre>
<p>Now when you access the <code data-enlighter-language="generic" class="EnlighterJSRAW">$user-&gt;lastLogin</code> attribute anywhere in your app, you&#8217;ll get the date in the user&#8217;s timezone.</p>
<p>The UserTimezoneAware trait expects the model to have a <code data-enlighter-language="generic" class="EnlighterJSRAW">timezone</code> attribute. If your column/attribute is named something other than <code data-enlighter-language="generic" class="EnlighterJSRAW">timezone</code> simply override the <code data-enlighter-language="generic" class="EnlighterJSRAW">$timezoneAttribute</code> property on the <code data-enlighter-language="generic" class="EnlighterJSRAW">UserTimezoneAware</code> trait.</p>
<pre data-enlighter-language="php" class="EnlighterJSRAW">
&lt;pre class=&quot;lang:php decode:true&quot;&gt;namespace App\Models;

use App\Traits\UserTimezoneAware
 
class User
{
    use UserTimezoneAware;

    public $timezoneAttribute= &#039;time_zone&#039;;
}&lt;/pre&gt;
</pre>
<p>Enjoy!</p><p>The post <a href="https://anderly.com/2017/12/21/laravel-user-timezone-aware-trait/">Laravel User Timezone Aware Trait</a> first appeared on <a href="https://anderly.com">Adam Anderly</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://anderly.com/2017/12/21/laravel-user-timezone-aware-trait/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Useful Laravel Model Traits: Gravatar</title>
		<link>https://anderly.com/2016/09/09/useful-laravel-traits-gravatar/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=useful-laravel-traits-gravatar</link>
					<comments>https://anderly.com/2016/09/09/useful-laravel-traits-gravatar/#respond</comments>
		
		<dc:creator><![CDATA[Adam]]></dc:creator>
		<pubDate>Fri, 09 Sep 2016 14:38:45 +0000</pubDate>
				<category><![CDATA[Laravel]]></category>
		<category><![CDATA[Laravel Traits]]></category>
		<guid isPermaLink="false">http://anderly.com/?p=520</guid>

					<description><![CDATA[<p>Starting a little series here on useful Laravel model traits. This first one I use quite often. // app\Traits\HasGravatar.php namespace App\Traits; trait HasGravatar { /** * The attribute name containing the email address. * * @var string */ public $gravatarEmail = 'email'; /** * Get the model's gravatar * * @return string */ public function [&#8230;]</p>
<p>The post <a href="https://anderly.com/2016/09/09/useful-laravel-traits-gravatar/">Useful Laravel Model Traits: Gravatar</a> first appeared on <a href="https://anderly.com">Adam Anderly</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>Starting a little series here on <a href="http://anderly.com/category/laravel-traits/" target="_blank" rel="noopener noreferrer">useful Laravel model traits</a>.</p>
<p>This first one I use quite often.</p>
<pre class="lang:php decode:true">// app\Traits\HasGravatar.php
namespace App\Traits;

trait HasGravatar
{
    
    /**
     * The attribute name containing the email address.
     *
     * @var string
     */
    public $gravatarEmail = 'email';
    
    /**
     * Get the model's gravatar
     *
     * @return string
     */
    public function getGravatarAttribute()
    {
        $hash = md5(strtolower(trim($this-&gt;attributes[$this-&gt;gravatarEmail])));
        return "https://www.gravatar.com/avatar/$hash";
    }

}
</pre>
<p>It&#8217;s a simple trait you can drop onto one of your eloquent models to add gravatar support like so:</p>
<pre class="lang:php decode:true">// app\Models\User.php
namespace App\Models;

use App\Traits\HasGravatar

class User
{
    use HasGravatar;
}
</pre>
<p>Now you can do something like this in your blade views:</p>
<pre class="lang:php decode:true">&lt;img src="{{ Auth::user()-&gt;gravatar }}"&gt;

</pre>
<p>And you&#8217;ll get the gravatar.com URL you&#8217;re expecting. No need to create a gravatar column, variable, method, or anything else.</p>
<p><code><code></code></code></p>
<p>The HasGravatar Trait expects the model to have an <code data-enlighter-language="generic" class="EnlighterJSRAW">email</code> attribute. If your column/attribute is named something other than <code data-enlighter-language="generic" class="EnlighterJSRAW">email</code> simply override the <code data-enlighter-language="generic" class="EnlighterJSRAW">$gravatarEmail</code> property on the <code data-enlighter-language="generic" class="EnlighterJSRAW">HasGravatar</code> trait.</p>
<p><code><code></code></code></p>
<pre class="lang:php decode:true">namespace App\Models;

use App\Traits\HasGravatar

class User
{
    use HasGravatar;

    public $gravatarEmail = 'email_address';
}
</pre>
<p><code><code></code></code></p>
<p>Enjoy!</p><p>The post <a href="https://anderly.com/2016/09/09/useful-laravel-traits-gravatar/">Useful Laravel Model Traits: Gravatar</a> first appeared on <a href="https://anderly.com">Adam Anderly</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://anderly.com/2016/09/09/useful-laravel-traits-gravatar/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Laravel Transformable &#8211; An Eloquent Model Trait for Consumable Models</title>
		<link>https://anderly.com/2016/09/08/laravel-transformable-a-model-trait-for-consumable-models/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=laravel-transformable-a-model-trait-for-consumable-models</link>
					<comments>https://anderly.com/2016/09/08/laravel-transformable-a-model-trait-for-consumable-models/#comments</comments>
		
		<dc:creator><![CDATA[Adam]]></dc:creator>
		<pubDate>Thu, 08 Sep 2016 21:37:31 +0000</pubDate>
				<category><![CDATA[Laravel]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[REST]]></category>
		<category><![CDATA[APIs]]></category>
		<guid isPermaLink="false">http://anderly.com/?p=518</guid>

					<description><![CDATA[<p>In working on a new Laravel-based REST API over an existing database, I quickly realized the need to shield API consumers from my underlying database structure, especially since I knew the structure would be changing. This is a common concern in API design and many times I would turn to creating DTOs (Data Transfer Objects) [&#8230;]</p>
<p>The post <a href="https://anderly.com/2016/09/08/laravel-transformable-a-model-trait-for-consumable-models/">Laravel Transformable – An Eloquent Model Trait for Consumable Models</a> first appeared on <a href="https://anderly.com">Adam Anderly</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>In working on a new Laravel-based REST API over an existing database, I quickly realized the need to shield API consumers from my underlying database structure, especially since I knew the structure would be changing. This is a common concern in API design and many times I would turn to creating DTOs (Data Transfer Objects) or transformer classes using either a scaled-down Transformer pattern or the excellent&nbsp;<a href="http://fractal.thephpleague.com/" target="_blank">Fractal</a> package. However, for this project time was of the essence so I needed a very quick way to simply provide a transformation map without having to create new transformer classes for each model and without having to update the <code>visible</code>&nbsp;and <code class="language-php">appends</code>&nbsp;attributes on every model.</p>
<p>Hence, the <code class="language-php">Transformable</code>&nbsp;trait was born. It&#8217;s a simple PHP trait that you drop into your model and then override the <code class="language-php">$transformation_map</code>&nbsp;property to specify your consumable model attributes.</p>
<p>Here&#8217;s an example:</p>
<pre class="lang:php decode:true">namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use App\Traits\Transformable;

class Group extends Model
{
    use Transformable;

    protected $transformation_map = [
        'Group_ID' =&gt; 'id',
        'Name' =&gt; 'name',
        'PublicGroup' =&gt; 'public',
    ];
}
</pre>
<p>A quick explanation: we&#8217;re importing the Transformable trait (which I&#8217;ll cover shortly) and then we&#8217;re providing a database column name-to-consumable attribute name mapping. That&#8217;s it.</p>
<p>A consumer of this model will not see the underlying database column names on the left hand of the mapping such as &#8220;Group_ID&#8221;, &#8220;Name&#8221;, or &#8220;PublicGroup.&#8221; Instead, they&#8217;ll see the names on the right hand side: &#8220;id&#8221;, &#8220;name&#8221; and &#8220;public.&#8221; In essence, it&#8217;s a simple transformer.&nbsp;Here&#8217;s an example usage:</p>
<pre class="lang:php decode:true">use App\Models\Group;

class GroupsController
{
    public function index()
    {
        return Group::all();
    }
}
</pre>
<p>With a call to the above <code class="language-php">GroupsController@index</code> method, we&#8217;d end up with the following JSON:</p>
<pre class="lang:php decode:true">[
    {
        "id": 1,
        "name": "Group 1",
        "public": "0"
    },
    {
        "id": 2,
        "name": "Group 2",
        "public": "0"
    }
}
</pre>
<p>Additionally, we can create the model specifying the consumable attribute names (not the database column names):</p>
<pre class="lang:php decode:true">$group = new App\Models\Group(['name' =&gt; 'Group 3', 'public' =&gt; true]);

// Calling this
$group-&gt;toJson();

// Will yield this
{
    "id": 3,
    "name": "Group 3",
    "public":"1"
}
</pre>
<p>The magic of this is in the Transformable trait. Normally, to achieve this, we&#8217;d have to specify the <code class="language-php">$visible</code> and <code class="language-php">$appends</code> properties on our model and provide&nbsp;get and set mutators for each attribute mapping. Instead, with Transformable, we simply provide the <code class="language-php">$transformation_map</code> and the Transformable trait takes care of the rest.</p>
<p>It does this by overriding several methods of the Eloquent model that determine if get and set mutators exist to include checking the <code data-enlighter-language="generic" class="EnlighterJSRAW">$transformation_map</code> property. This is how it can return you an instance of your model as well as create an instance of your model with the consumable attribute names without you needing to specify&nbsp;the source database column names.</p>
<p>I&#8217;m working on an enhancement to Transformable to allow queries (i.e. where* methods) on the models so that you can query with the consumable attribute names and&nbsp;not the database column names. As soon as I wrap up those changes, I&#8217;ll look at making this a composer package.</p>
<p>Here&#8217;s the entire trait implementation:</p>
<pre class="lang:php decode:true">namespace App\Traits;

use Illuminate\Support\Str;

Trait Transformable
{
	protected $transformation_map = [];

    /**
     * Determine if a get mutator exists for an attribute.
     *
     * @param  string  $key
     * @return bool
     */
    public function hasGetMutator($key)
    {
        return $this-&gt;keyExistsInTransformationMap($key) || parent::hasGetMutator($key);
    }

    /**
     * Determine if a set mutator exists for an attribute.
     *
     * @param  string  $key
     * @return bool
     */
    public function hasSetMutator($key)
    {
        return $this-&gt;keyExistsInTransformationMap($key) || parent::hasSetMutator($key);
    }

    /**
     * Get the value of an attribute using its mutator.
     *
     * @param  string  $key
     * @param  mixed  $value
     * @return mixed
     */
    protected function mutateAttribute($key, $value)
    {
        if ($this-&gt;keyExistsInTransformationMap($key)) {
            return $this-&gt;{$this-&gt;getTransformationMapInverse()[$key]};
        }
        return parent::mutateAttribute($key, $value);
    }

    /**
     * Set a given attribute on the model.
     *
     * @param  string  $key
     * @param  mixed  $value
     * @return $this
     */
    public function setAttribute($key, $value)
    {
        if ($this-&gt;keyExistsInTransformationMap($key)) {
            if (method_exists($this, 'set'.Str::studly($key).'Attribute')) {
                parent::setAttribute($key, $value);
            }
            $key = $this-&gt;getTransformationMapInverse()[$key];
        }
        return parent::setAttribute($key, $value);
    }

    public function keyExistsInTransformationMap($key)
    {
        return array_key_exists($key, $this-&gt;getTransformationMapInverse());
    }

    public function getTransformationMapInverse()
    {
        return array_flip($this-&gt;transformation_map);
    }
}
</pre>
<p>This could certainly be enhanced further to support casting to/from datatypes and supporting compound properties via a Closure or similar.</p>
<p>Let me know what you think in the comments.</p><p>The post <a href="https://anderly.com/2016/09/08/laravel-transformable-a-model-trait-for-consumable-models/">Laravel Transformable – An Eloquent Model Trait for Consumable Models</a> first appeared on <a href="https://anderly.com">Adam Anderly</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://anderly.com/2016/09/08/laravel-transformable-a-model-trait-for-consumable-models/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
	</channel>
</rss>
