How to Apply Custom Casting for Spatial Columns with Laravel and TarfinLabs\LaravelSpatial in Select Queries?
Issue: I have a users table with the following columns: id, name, email, status (TinyInt), and location (Point). I'm using custom casting for the status column, which works perfectly, even when using the select method on the model.
class UserStatusCast implements CastsAttributes
{
public function get(Model $model, string $key, mixed $value, array $attributes): mixed
{
return UserStatusEnum::from($value)->label();
}
public function set(Model $model, string $key, mixed $value, array $attributes): mixed
{
return UserStatusEnum::fromLabel($value);
}
}
enum UserStatusEnum: int
{
case Inactive = 0;
case Active = 1;
case Ban = 2;
public function label() {
return match ($this) {
self::Inactive => 'inactive',
self::Active => 'active',
self::Ban => 'ban',
};
}
public static function fromLabel(string $label) {
return match ($label) {
'inactive' => self::Inactive,
'active' => self::Active,
'ban' => self::Ban,
default => throw new Exception('Invalid user status provided'),
};
}
}
However, I'm using the TarfinLabs\LaravelSpatial library for casting and handling spatial data (e.g., the location column as Point), and I'm experiencing an issue where the custom LocationCast does not work with the select method on the model.
class LocationCast implements CastsAttributes, SerializesCastableAttributes
{
public function get($model, string $key, $value, array $attributes): ?Point
{
if (is_null($value)) {
return null;
}
$coordinates = explode(',', $value);
$location = explode(',', str_replace(['POINT(', ')', ' '], ['', '', ','], $coordinates[0]));
return new Point(lat: (float) $location[1], lng: (float) $location[0]);
}
public function set($model, string $key, $value, array $attributes): Expression
{
if (!$value instanceof Point) {
throw new InvalidArgumentException(sprintf('The %s field must be instance of %s', $key, Point::class));
}
return DB::raw("ST_GeomFromText('{$value->toWkt()}')");
}
public function serialize($model, string $key, $value, array $attributes): array
{
return $value->toArray();
}
}
Problem: When using the select method on the User model, the LocationCast does not work as expected. Here's the query I initially used to retrieve the data:
User::select(['id', 'full_name', 'email', 'phone_number', 'role', 'status'])
This works fine for the status column, but the location column is not being cast properly, and the spatial data (Point) isn't returned as expected. To fix this, I have resorted to using a raw SQL query as a workaround:
User::select([
'id',
'full_name',
'email',
'phone_number',
'role',
DB::raw("CONCAT(ST_AsText(users.location),ST_SRID(users.location)) as location"),
'status'
])
What I need: This raw SQL query is not clean or authentic. I would like to find a better, cleaner solution for applying the custom casting on the location column, even when using the select method.
Has anyone encountered a similar issue or found a better approach to get spatial data (Point) correctly casted in Laravel with TarfinLabs\LaravelSpatial? Any help would be greatly appreciated!
Please or to participate in this conversation.