CoderFunda
  • Home
  • About us
    • Contact Us
    • Disclaimer
    • Privacy Policy
    • About us
  • Home
  • Php
  • HTML
  • CSS
  • JavaScript
    • JavaScript
    • Jquery
    • JqueryUI
    • Stock
  • SQL
  • Vue.Js
  • Python
  • Wordpress
  • C++
    • C++
    • C
  • Laravel
    • Laravel
      • Overview
      • Namespaces
      • Middleware
      • Routing
      • Configuration
      • Application Structure
      • Installation
    • Overview
  • DBMS
    • DBMS
      • PL/SQL
      • SQLite
      • MongoDB
      • Cassandra
      • MySQL
      • Oracle
      • CouchDB
      • Neo4j
      • DB2
      • Quiz
    • Overview
  • Entertainment
    • TV Series Update
    • Movie Review
    • Movie Review
  • More
    • Vue. Js
    • Php Question
    • Php Interview Question
    • Laravel Interview Question
    • SQL Interview Question
    • IAS Interview Question
    • PCS Interview Question
    • Technology
    • Other

25 August, 2022

Eloquent Attribute Casting

 Programing Coderfunda     August 25, 2022     No comments   

 Eloquent Castable attributes are one of the more powerful features of Laravel; some people use them religiously while others tend to shy away from them. In this tutorial, I will walk through a few different examples of using them and, most importantly, why you should be using them.

You can create a new Laravel project or use an existing one for this tutorial. Feel free to follow along with me, or if you want to read and remember - that is fine too. The critical thing to take away from this is just how good eloquent casts can be.

Let's start with our first example, similar to the one in the Laravel documentation. The Laravel documentation shows getting a user's address but using a Model too. Now imagine if this was instead a JSON column on the user or any model for that fact. We can get and set some data and add extra to make this work as we want. We need to install a package by Jess Archer called Laravel Castable Data Transfer Object. You can install this using the following composer command:

1composer require jessarcher/laravel-castable-data-transfer-object

Now we have this installed, let's design our Castable class:

1namespace App\Casts;
2 
3use JessArcher\CastableDataTransferObject\CastableDataTransferObject;
4 
5class Address implements CastableDataTransferObject
6{
7 public string $nameOrNumber,
8 public string $streetName,
9 public string $localityName,
10 public string $town,
11 public string $county,
12 public string $postalCode,
13 public string $country,
14}

We have a PHP class that extends the CastableDataTransferObject, which allows us to mark the properties and their types, then handles all the getting and setting options behind the scenes. This package uses a Spatie package called Data Transfer Object under the hood, which has been quite popular in the community. So now, if we wanted to extend this at all, we could:

1namespace App\Casts;
2 
3use JessArcher\CastableDataTransferObject\CastableDataTransferObject;
4 
5class Address implements CastableDataTransferObject
6{
7 public string $nameOrNumber,
8 public string $streetName,
9 public string $localityName,
10 public string $town,
11 public string $county,
12 public string $postalCode,
13 public string $country,
14 
15 public function formatString(): string
16 {
17 return implode(', ', [
18 "$this->nameOrNumber $this->streetName",
19 $this->localityName,
20 $this->townName,
21 $this->county,
22 $this->postalCode,
23 $this->country,
24 ]);
25 }
26}

We have an Address class that extends the CastableDataTransferObject class from the package, which handles all of our getting and setting of data to the database. We then have all the properties we want to store - this is the UK format address as it is where I live. Finally, we have a method that we have added that helps us format this address as a string - if we want to display this in any form of the user interface. We could take it a step further with postcode validation using regex or an API - but that might defeat the point of the tutorial.

Let's move on to another example: money. We all understand money; we know that we can use money in its smallest form of coins and its larger form of notes. So imagine we have an e-commerce store where we store our products (a simple store so we don't have to worry about variants, etc.), and we have a column called price which we store in the smallest denominator of our currency. I am in the UK, so I will call this pence. However, this in the US is cents. A common approach to storing monetary values in our database as it avoids floating-point math issues. So let's design a cast for this price column, this time using the php moneyphp/money package:

1namespace App\Casts;
2 
3use Money\Currency;
4use Money\Money;
5use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
6 
7class Money implements CastsAttributes
8{
9 public function __construct(
10 protected int $amount,
11 ) {}
12 
13 public function get($model, string $key, $value, array $attributes)
14 {
15 return new Money(
16 $attributes[$this->amount],
17 new Currency('GBP'),
18 );
19 }
20 
21 public function set($model, string $key, $value, array $attributes)
22 {
23 return [
24 $this->amount => (int) $value->getAmount(),
25 $this->curreny => (string) $value->getCurrency(),
26 ];
27 }
28}

So this class doesn't use the DTO package but instead returns a new instance with its own methods. In our constructor, we pass in an amount. When we get and set the amount, we are either casting to an array for storage in a database or parsing an array to return a new money object. Of course, we could make this a data transfer object instead and control how we handle money a little more. However, the php money library is pretty well tested and reliable, and if I am honest - there isn't much I would do differently.

Let's go to a new example. We have a CRM application, and we want to store business open hours or days. Each business needs to be able to mark the days they are open, but only in a simple true or false way. So we can create a new cast, but first, we will make the class that we want to cast to, much like the money PHP class above.

1namespace App\DataObjects;
2 
3class Open
4{
5 public function __construct(
6 public readonly bool $monday,
7 public readonly bool $tuesday,
8 public readonly bool $wednesday,
9 public readonly bool $thursday,
10 public readonly bool $friday,
11 public readonly bool $saturday,
12 public readonly bool $sunday,
13 public readonly array $holidays,
14 ) {}
15}

To begin with, this is fine; we just want to store if the business is open each day and have an array of days they count as holidays. Next, we can design our cast:

1namespace App\Casts;
2 
3use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
4 
5class OpenDaysCast implements CastsAttributes
6{
7 public function __construct(
8 public readonly array $dates,
9 ) {}
10 
11 public function set($model, string $key, $value, array $attributes)
12 {
13 return $this->dates;
14 }
15 
16 public function get($model, string $key, $value, array $attributes)
17 {
18 return Open::fromArray(
19 dates: $this->dates,
20 );
21 }
22}

Our constructor will accept an array of dates to simplify the saving (however, you will need to make sure you validate input properly here). Then when we want to get the data back out from the database, we create a new Open object, passing in the dates. However, here we are calling a fromArray method that we have yet to create, so let's design that now:

1public static function fromArray(array $dates): static
2{
3 return new static(
4 monday: (bool) data_get($dates, 'monday'),
5 tuesday: (bool) data_get($dates, 'tuesday'),
6 wednesday: (bool) data_get($dates, 'wednesday'),
7 thursday: (bool) data_get($dates, 'thursday'),
8 friday: (bool) data_get($dates, 'friday'),
9 saturday: (bool) data_get($dates, 'saturday'),
10 sunday: (bool) data_get($dates, 'sunday'),
11 holidays: (array) data_get($dates, 'holidays'),
12 );
13}

So we manually build up our Open object using the Laravel helper data_get, which is extremely handy, making sure that we are casting to the correct type. Now when we query, we have access:

1$business = Business:query()->find(1);
2 
3// Is this business open on mondays?
4$business->open->monday; // true|false
5 
6// Is the business open on tuesdays?
7$business->open->tuesday; // true|false
8 
9// What are the busines holiday dates?
10$business->open->holidays; // array

As you can see, we can make this extremely readable so that the developer experience is logical and easy to follow. Can we then extend this to add additional methods, such as is it open today?

1public function today(): bool
2{
3 $date = now();
4 
5 $day = strtolower($date->englishDayOfWeek());
6 
7 if (! $this->$day) {
8 return false;
9 }
10 
11 return ! in_array(
12 $date->toDateString(),
13 $this->holidays,
14 );
15}

So this method may make a small assumption that we are storing holidays in a date string, but the logic follows: get the day of the week, in English, as that is our property name; if the day is false, then return false. However, we also need to check the holidays. If the current date string is not in the holiday's array, it is open; otherwise, it is closed.

So we can then check to see if the business is open using the today method on our cast:

1$business = Business:query()->find(1);
2 
3// Is the business open today?
4$business->open->today();

As you can probably imagine, you can add many methods to this, allowing you to check multiple things and even add ways to display this information nicely.

Do you use Attribute casting in your application? Do you have any other cool examples you could share? Please drop us a tweet with your examples and share the power of knowledge.

  • Share This:  
  •  Facebook
  •  Twitter
  •  Google+
  •  Stumble
  •  Digg
Email ThisBlogThis!Share to XShare to Facebook

Related Posts:

  • Can't run jupyter using docker compose with nginxi am trying to run python script on jupyter using docker compose but can't connect to python kernel and this is my docker compose file which running o… Read More
  • offset formula / dynamic range in Google sheetsI need to set a dynamic range/offset formula in order to track only the last 5 inputs in to separate fields (for each person). Each month I am adding … Read More
  • Conditionally Assert Throwing An Exception in Pest--- Pest recently added the throwsUnless() method in Pest v2.24 to conditionally verify an exception if a given boolean expression evaluates to fa… Read More
  • VSCode IntelliSense autocomplete suddenly stopped working with node modulesI'm trying to make a steam bot with JS, and IntelliSense does not work. I have my SteamUser object declared: const SteamUser = require("steam-use… Read More
  • Outlook Showing in Process even after closing - C#I am writing a code to such that I will get a trigger when ever outlook opens Process[] processlist = System.Diagnostics.Process.GetProcessesByName("O… Read More
Newer Post Older Post Home

0 comments:

Post a Comment

Thanks

Meta

Popular Posts

  • Spring boot app (error: method getFirst()) failed to run at local machine, but can run on server
    The Spring boot app can run on the online server. Now, we want to replicate the same app at the local machine but the Spring boot jar file f...
  • Log activity in a Laravel app with Spatie/Laravel-Activitylog
      Requirements This package needs PHP 8.1+ and Laravel 9.0 or higher. The latest version of this package needs PHP 8.2+ and Laravel 8 or hig...
  • Failed to install 'cordova-plugin-firebase': CordovaError: Uh oh
    I had follow these steps to install an configure firebase to my cordova project for cloud messaging. https://medium.com/@felipepucinelli/how...
  • Laravel auth login with phone or email
          <?php     Laravel auth login with phone or email     <? php     namespace App \ Http \ Controllers \ Auth ;         use ...
  • Cashier package and Blade files
    I'm a little confused about this Cashier package. I installed it using the Laravel website (with composer), but noticed there's no...

Categories

  • Ajax (26)
  • Bootstrap (30)
  • DBMS (42)
  • HTML (12)
  • HTML5 (45)
  • JavaScript (10)
  • Jquery (34)
  • Jquery UI (2)
  • JqueryUI (32)
  • Laravel (1017)
  • Laravel Tutorials (23)
  • Laravel-Question (6)
  • Magento (9)
  • Magento 2 (95)
  • MariaDB (1)
  • MySql Tutorial (2)
  • PHP-Interview-Questions (3)
  • Php Question (13)
  • Python (36)
  • RDBMS (13)
  • SQL Tutorial (79)
  • Vue.js Tutorial (68)
  • Wordpress (150)
  • Wordpress Theme (3)
  • codeigniter (108)
  • oops (4)
  • php (853)

Social Media Links

  • Follow on Twitter
  • Like on Facebook
  • Subscribe on Youtube
  • Follow on Instagram

Pages

  • Home
  • Contact Us
  • Privacy Policy
  • About us

Blog Archive

  • September (100)
  • August (50)
  • July (56)
  • June (46)
  • May (59)
  • April (50)
  • March (60)
  • February (42)
  • January (53)
  • December (58)
  • November (61)
  • October (39)
  • September (36)
  • August (36)
  • July (34)
  • June (34)
  • May (36)
  • April (29)
  • March (82)
  • February (1)
  • January (8)
  • December (14)
  • November (41)
  • October (13)
  • September (5)
  • August (48)
  • July (9)
  • June (6)
  • May (119)
  • April (259)
  • March (122)
  • February (368)
  • January (33)
  • October (2)
  • July (11)
  • June (29)
  • May (25)
  • April (168)
  • March (93)
  • February (60)
  • January (28)
  • December (195)
  • November (24)
  • October (40)
  • September (55)
  • August (6)
  • July (48)
  • May (2)
  • January (2)
  • July (6)
  • June (6)
  • February (17)
  • January (69)
  • December (122)
  • November (56)
  • October (92)
  • September (76)
  • August (6)

  • Failed to install 'cordova-plugin-firebase': CordovaError: Uh oh - 9/21/2024
  • pyspark XPath Query Returns Lists Omitting Missing Values Instead of Including None - 9/20/2024
  • SQL REPL from within Python/Sqlalchemy/Psychopg2 - 9/20/2024
  • MySql Explain with Tobias Petry - 9/20/2024
  • How to combine information from different devices into one common abstract virtual disk? [closed] - 9/20/2024

Laravel News

  • Filter Model Attributes with Laravel's New except() Method - 5/13/2025

Copyright © 2025 CoderFunda | Powered by Blogger
Design by Coderfunda | Blogger Theme by Coderfunda | Distributed By Coderfunda