php - Laravel Submodel Of an Model

In my Laravel application, I need submodels of the base ORM model, for specific types of item in my DB which is specified in 'type' column in database.

In my base model, I use this override for function newFromBuilder

// OVERRIDES
public function newFromBuilder($attributes = [], $connection = null)
{
    $class = "\\App\\Models\\" . ucfirst($attributes->type);

    if (class_exists($class)) {
        $model = new $class();
    } else {
        $model = $this->newInstance([], true);
    }

    $model->setRawAttributes((array)$attributes, true);
    $model->setConnection($connection ?: $this->getConnectionName());
    $model->fireModelEvent('retrieved', false);

    return $model;
}

but for some reason when i call save or update function in submodel nothing happen :( Each submodel should ingered save function of fase model s that right ? Could anybody help me to fix issue with save function ?

Base model:

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Carbon;

use App\Models\Devices;
use App\Models\Records;
use App\Models\Rooms;

use App\Helpers\SettingManager;
use App\Types\GraphPeriod;
use App\Types\PropertyType;

class Properties extends Model
{
    protected $fillable = [];
    protected $table = 'sh_properties';
    protected $primaryKey = 'id';

    public $period = GraphPeriod::DAY;

    //OVERIDES
    public function newFromBuilder($attributes = [], $connection = null)
    {
        $class = "\\App\\Models\\" . ucfirst($attributes->type);

        if (class_exists($class)) {
            $model = new $class();
        } else {
            $model = $this->newInstance([], true);
        }

        $model->setRawAttributes((array)$attributes, true);
        $model->setConnection($connection ?: $this->getConnectionName());
        $model->fireModelEvent('retrieved', false);

        return $model;
    }


    //NEW RELATIONS
    public function records()
    {
        return $this->hasMany(Records::class, 'property_id');
    }

    public function latestRecord()
    {
        return $this->hasOne(Records::class, 'property_id')->latestOfMany();
    }

    public function device()
    {
        return $this->belongsTo(Devices::class);
    }

    public function room()
    {
        return $this->belongsTo(Rooms::class);
    }

    public function settings()
    {
        if ($settings = SettingManager::getGroup('property-' . $this->id)) {
            return $settings;
        }
        return false;
    }

    //FUNCTIONS
    public function getLatestRecordNotNull()
    {
        return Records::where("property_id", $this->id)->where("value", "!=", null)->where("value", "!=", 0)->first();
    }

    //Virtual  Values
    use HasFactory;


    //Add Function for mutator for vaue (vith units) and rav value















    public function values()
    {
        $dateFrom = Carbon::now()->subDays(1);

        switch ($this->period) {
            case GraphPeriod::WEEK:
                $dateFrom = Carbon::now()->subWeek(1);
                break;
            case GraphPeriod::MONTH:
                $dateFrom = Carbon::now()->subMonth(1);
                break;
            case GraphPeriod::YEAR:
                $dateFrom = Carbon::now()->subYear(1);
                break;
        }

        return $this->hasMany(Records::class, 'property_id')->whereDate('created_at', '>', $dateFrom)->orderBy('created_at', 'DESC');
    }

    public function getAgregatedValuesAttribute($period = GraphPeriod::DAY)
    {
        $dateFrom = Carbon::now()->subDays(1);
        $periodFormat = "%Y-%m-%d %hh";

        switch ($this->period) {
            case GraphPeriod::WEEK:
                $dateFrom = Carbon::now()->subWeek(1);
                $periodFormat = "%Y-%m-%d";
                break;
            case GraphPeriod::MONTH:
                $dateFrom = Carbon::now()->subMonth(1);
                $periodFormat = "%Y-%m-%d";
                break;
            case GraphPeriod::YEAR:
                $dateFrom = Carbon::now()->subYear(1);
                $periodFormat = "%Y-%m";
                break;
        }

        $agregatedData = Records::select(['value', 'done', 'created_at'])
            ->selectRaw("DATE_FORMAT(created_at, ?) as period", [$periodFormat])
            ->selectRaw("ROUND(MIN(value), 1) AS min")
            ->selectRaw("ROUND(MAX(value), 1) AS max")
            ->selectRaw("ROUND(AVG(value), 1) AS value")
            ->where('property_id', $this->id)
            ->orderBy('created_at', 'DESC')
            ->groupBy('period');

        $agregatedData->where('created_at', '>=', $dateFrom);
        return $agregatedData->get();
    }

    public function last_value()
    {
        return $this->hasOne(Records::class, 'property_id', 'id')->latest();
    }




    //Virtual  Values


    //Virtual  Values
    /**
     * Minimum value that property had in past.
     *
     * @return int
     */
    public function getMaxValueAttribute()
    {
        if ($this->records) {
            return $this->records->max("value");
        }
        return false;
    }

    /**
     * Maximum value that property had in past.
     *
     * @return int
     */
    public function getMinValueAttribute()
    {
        if ($this->records) {
            return $this->records->min("value");
        }
        return false;
    }


    /**
     * step value used to increment each value usually used for range type or thermostats, graphs also.
     *
     * @return int
     */
    public function getStepValueAttribute()
    {
        if ($step = SettingManager::get('step', 'property-' . $this->id)) {
            return ($step->value < 1 ? $step->value : 1);
        }
        return false;
    }

    /**
     * max set value for prop
     *
     * @return int
     */
    public function getMaxValueSettingAttribute()
    {
        if ($step = SettingManager::get('max', 'property-' . $this->id)) {
            return ($step->value > 1 ? $step->value : 1);
        }
        return false;
    }

    /**
     * min set value for prop
     *
     * @return int
     */
    public function getMinValueSettingAttribute()
    {
        if ($step = SettingManager::get('min', 'property-' . $this->id)) {
            return ($step->value > 1 ? $step->value : 1);
        }
        return false;
    }


    public function setValue($value)
    {
        $record                 = new Records;
        $record->value          = $value;
        $record->property_id    = $this->id;
        $record->save();
        return true;
    }
}

Submodel

namespace App\Models;

use App\Models\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Humi extends Properties
{
    protected $historyDefault = 90;
    protected $unitsDefault = "%";
    protected $iconDefault = "";

    public function save(array $options = [])
    {
        // before save code 
        $result = parent::save($options); // returns boolean
        // after save code
        return $result; // do not ignore it eloquent calculates this value and returns this, not just to ignore
    }
}

Thank you in advance for any suggestions or help :)

Answer

Solution:

When a new instance is created with the $this->newInstance() function, the $model->exists property is set to true. I think you should do the same in your if statement as well. Otherwise it will try to create a new record in the database.

It might be a good idea to copy the rest of the function as well to avoid any other problems it may cause.

if (class_exists($class)) {
    $model = new $class();

    // Important
    $model->exists = true;

    $model->setTable($this->getTable());

    $model->mergeCasts($this->casts);
} else {
    $model = $this->newInstance([], true);
}

Source