Skip to content

Instantly share code, notes, and snippets.

@Artistan
Created November 5, 2018 16:21
Show Gist options
  • Save Artistan/50cc06379fc2ab829ad00313fd0a8b61 to your computer and use it in GitHub Desktop.
Save Artistan/50cc06379fc2ab829ad00313fd0a8b61 to your computer and use it in GitHub Desktop.
Make a model dynamically appendable.

Inspired by https://stackoverflow.com/questions/47222168/setappends-on-relation-which-is-being-loaded-by-with-in-laravel/53158181#53158181

setAppends([]) on relation which is being loaded by with in Laravel

The given solution does not work when using a package that does a lot of the work after you define the with() relations like datatables

here is a solution that works for any model.

<?php

namespace App\Database;

trait Appendable {

    static protected $static_appends = [];
    static protected $static_replace_appends = null;

    /**
     * set a static appends array to add to or replace the existing appends array..
     * replace => totally replaces the existing models appends array at time of calling getArrayableAppends
     * add => merges and then makes unique. when getArrayableAppends is called. also merges with the existing static_appends array
     *
     * @param $appendsArray
     * @param bool $replaceExisting
     */
    public static function setStaticAppends($appendsArray, $replaceExisting = true)
    {
        if($replaceExisting) {
            static::$static_replace_appends = true;
            static::$static_appends = array_unique($appendsArray);
        } else {
            static::$static_replace_appends = false;
            static::$static_appends = array_unique(array_merge(static::$static_appends,$appendsArray));
        }
    }

    /**
     * Get all of the appendable values that are arrayable.
     *
     * @return array
     */
    protected function getArrayableAppends()
    {
        if(!is_null(static::$static_replace_appends)) {
            if(static::$static_replace_appends) {
                $this->appends = array_unique(array_merge(static::$static_appends,$this->appends??[]));
            } else {
                $this->appends = static::$static_appends;
            }
        }
        return parent::getArrayableAppends();
    }

}

then you can just apply the trait to any model

<?php

namespace App\Database;

abstract class Company
{
    use Appendable;
}

then call the static method BEFORE you use the relationship

<?php

$replaceCurrentAppendsArray = true;
// this will remove the original appends by replacing with empty array
\App\Database\Company::setStaticAppends([],$replaceCurrentAppendsArray);

$replaceCurrentAppendsArray = true;
// this will remove the original appends by replacing with smaller array
\App\Database\Company::setStaticAppends(['thumbnail_url'],$replaceCurrentAppendsArray);

$replaceCurrentAppendsArray = FALSE;
// this will add to the original appends by providing an additional array element
\App\Database\Company::setStaticAppends(['my_other_attribute'],$replaceCurrentAppendsArray);

this will allow you to override the appends array provided on the model even if another package is going to be loading the model. Like yajra/laravel-datatable where my issue was and brought me to this page which inspired a more dynamic solution.

This is similar to Stefan's second approach, but this is more dynamic so you do not have to create additional model extensions to accomplish the overrides.

You could take a similar approach to override the HidesAttribute trait as well.

<?php
namespace App\Database;
trait Appendable {
static protected $static_appends = [];
static protected $static_replace_appends = null;
/**
* set a static appends array to add to or replace the existing appends array..
* replace => totally replaces the existing models appends array at time of calling getArrayableAppends
* add => merges and then makes unique. when getArrayableAppends is called. also merges with the existing static_appends array
*
* @param $appendsArray
* @param bool $replaceExisting
*/
public static function setStaticAppends($appendsArray, $replaceExisting = true)
{
if($replaceExisting) {
static::$static_replace_appends = true;
static::$static_appends = array_unique($appendsArray);
} else {
static::$static_replace_appends = false;
static::$static_appends = array_unique(array_merge(static::$static_appends,$appendsArray));
}
}
/**
* Get all of the appendable values that are arrayable.
*
* @return array
*/
protected function getArrayableAppends()
{
if(!is_null(static::$static_replace_appends)) {
if(static::$static_replace_appends) {
$this->appends = array_unique(array_merge(static::$static_appends,$this->appends??[]));
} else {
$this->appends = static::$static_appends;
}
}
return parent::getArrayableAppends();
}
}
<?php
namespace App\Database;
abstract class Company
{
use Appendable;
}
<?php
$replaceCurrentAppendsArray = true;
// this will remove the original appends by replacing with empty array
\App\Database\Company::setStaticAppends([],$replaceCurrentAppendsArray);
$replaceCurrentAppendsArray = true;
// this will remove the original appends by replacing with smaller array
\App\Database\Company::setStaticAppends(['thumbnail_url'],$replaceCurrentAppendsArray);
$replaceCurrentAppendsArray = FALSE;
// this will add to the original appends by providing an additional array element
\App\Database\Company::setStaticAppends(['my_other_attribute'],$replaceCurrentAppendsArray);
@joy2fun
Copy link

joy2fun commented May 4, 2023

Works like a charm!
I think an ! mark should be added to line 37.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment