Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

jginorio's avatar

Cast model to another model when returning from database

Background Information

I have a model called Product. The product can have 3 different stock methods: Serialize, Bulk and None.

  • Serialize is chosen when every physical item has a barcode.
  • Bulk is chosen when we care for the changes in quantities
  • None is chosen when we want to include a product in the inventory but we don't care about the quantity.

What I want to do

I want to create those 3 models (Serialize, Bulk, None) and extend them to the Product. Then to be able to call something like Product::all() and get a collection like this:

Products [
   0   => Serialize[...]
   1   => Serialize[...]
   2   => Bulk[...]
   3   => None[...]
   ...
   500 => Bulk[...]
]

My problem

How can I make laravel cast each object to their respective classes automatically?

Database Schema Database Schema

My Model structure

Product model

class Product extends Model
{
	use HasFactory;

	/**
	 * The attributes that aren't mass assignable.
	 *
	 * @var array
	 */
	protected $guarded = ['id'];

	/**
	 * The attributes that should be cast to native types.
	 *
	 * @var array
	 */
	protected $casts = [
		'purchase_price' => MoneyCast::class,
		'rental_price'   => MoneyCast::class,
		'is_active'      => 'boolean',
	];

	/**
	 * Get active products only
	 *
	 * @param Builder $builder
	 *
	 * @return Builder
	 */
	public function scopeActive(Builder $builder): Builder
	{
		return $builder->where('is_active', '=', true);
	}

	/**
	 * Get inactive products only
	 *
	 * @param Builder $builder
	 *
	 * @return Builder
	 */
	public function scopeInactive(Builder $builder): Builder
	{
		return $builder->where('is_active', '=', false);
	}

	/**
	 * Set the name and slug when saving to database
	 *
	 * @param $value
	 */
	public function setNameAttribute($value): void
	{
		$this->attributes['name'] = $value;
		$this->attributes['slug'] = Str::slug($value);
	}

	/**
	 * Get the products full name
	 *
	 * @return string
	 */
	public function getFullNameAttribute(): string
	{
		return $this->details
			? "{$this->name} {$this->details}"
			: $this->name;
	}

	/**
	 * Calculate total stocks
	 *
	 * @return int
	 */
	abstract public function getTotalAttribute(): int;

	/**
	 * Calculate total stocks in quarantine
	 *
	 * @return int
	 */
	abstract public function getQuarantinedAttribute(): int;

	/**
	 * Calculate available stocks
	 *
	 * @return int
	 */
	public function getAvailableAttribute(): int
	{
		return $this->getTotalAttribute() - $this->getQuarantinedAttribute();
	}

	/**
	 * Get brand
	 *
	 * @return BelongsTo
	 */
	public function brand(): BelongsTo
	{
		return $this->belongsTo(Brand::class);
	}

	/**
	 * Get category
	 *
	 * @return BelongsTo
	 */
	public function category(): BelongsTo
	{
		return $this->belongsTo(Category::class);
	}

Serialize Model

class Serialize extends Product
{
/**
	 * Calculate total stocks
	 *
	 * @return int
	 */
	public function getTotalAttribute(): int
	{
		return $this->stocks()->count();
	}

    /**
	 * Calculate total stocks in quarantine
	 *
	 * @return int
	 */
	public function getQuarantinedAttribute(): int
	{
		return $this->stocks->sum(function ($stock) {
			return $stock->repairs()
			             ->where('status', '!=', last(Repair::STATUSES))
			             ->where('is_working', '=', false)
			             ->count();
		});
	}

	/**
	 * Get associated stocks
	 *
	 * @return HasMany
	 */
	public function stocks(): HasMany
	{
		return $this->hasMany(Stock::class)->orderBy('barcode');
	}
}

Bulk Model

class Bulk extends Product
{
    /**
	 * Calculate total stocks
	 *
	 * @return int
	 */
    public function getTotalAttribute(): int
	{
		return $this->transactions()->sum('quantity');
	}

    /**
	 * Calculate total stocks in quarantine
	 *
	 * @return int
	 */
	public function getQuarantinedAttribute(): int
	{
		return $this->stocks->sum(function ($stock) {
			return $stock->repairs()
			             ->where('status', '!=', last(Repair::STATUSES))
			             ->where('is_working', '=', false)
			             ->sum('quantity');
		});
	}

	/**
	 * Get bulk stock transactions.
	 *
	 * @return HasMany
	 */
	public function transactions(): HasMany
	{
		return $this->hasMany(Transaction::class)->latest();
	}
}
0 likes
1 reply

Please or to participate in this conversation.