Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Laravel Nova #28

Closed
mziraki opened this issue Feb 28, 2022 · 33 comments
Closed

Support Laravel Nova #28

mziraki opened this issue Feb 28, 2022 · 33 comments

Comments

@mziraki
Copy link

mziraki commented Feb 28, 2022

error:
Unable to encode attribute [original] for model [Laravel\Nova\Actions\ActionEvent] to JSON: Malformed UTF-8 characters, possibly incorrectly encoded.

@mziraki mziraki changed the title Laravel Nova Support Support Laravel Nova Feb 28, 2022
@MatanYadaev
Copy link
Owner

Hi @mziraki, thanks for opening this issue. I need some context in order to understand the issue. I don't work with Nova. Can you share some code? When the error occurs? Is that really related to the package?

@mziraki
Copy link
Author

mziraki commented Feb 28, 2022

@MatanYadaev hi, thanks for quick reply
I tried to migrate from grimzy/laravel-mysql-spatial#186 (comment) to this package.
it seems "laravel-mysql-spatial" converts fields in deeper level and when laravel nova wants to save the whole model for logging gets the correct Geometry object (e.g: Point) but with this package it gets the raw field and when it tries to encode it to json, produces the error. I wonder if it's possible to add the same conversion in deeper level for this package, too.

@mziraki
Copy link
Author

mziraki commented Feb 28, 2022

@MatanYadaev

e.g:

Exhibition::find(86)

dd with this package:
#original: array:23 [
.....
"location" => b"\x00\x00\x00\x00\x01\x01\x00\x00\x00™WæϹI@Âl\x19I\àA@"
....
]

dd with grimzy package:
#original: array:23 [
.....
"location" => Grimzy\LaravelMysqlSpatial\Types\Point {#2079 ▼
#lat: 35.752816331305
#lng: 51.4457340121
#srid: 0
}
.....
]

@MatanYadaev
Copy link
Owner

@mziraki I have no idea how can I help you with that. Feel free to send a PR if you find a solution.

@mziraki mziraki closed this as completed Mar 5, 2022
@Synchro
Copy link
Contributor

Synchro commented Apr 7, 2022

@mziraki I've just run into exactly the same problem, and I've asked about it in the Nova channel on Laracasts, but no answers yet :(

I wonder if it's trying to treat the WKB string as UTF-8, which will probably cause that error message, though it should not be trying to do that if the cast is being applied when it is fetched from the DB – which I have asked it to do.

@mziraki
Copy link
Author

mziraki commented Apr 7, 2022

@Synchro you can fork my fork (https://github.com/mziraki/laravel-mysql-spatial) and use:

"repositories": [
    {
        "type": "vcs",
        "url": "https://github.com/[your_username]/laravel-mysql-spatial.git"
    },
],

in your composer.json

@Synchro
Copy link
Contributor

Synchro commented Apr 8, 2022

@MatanYadaev the problem is that it's not casting the raw WKB value to an object. The code to reproduce that is very simple, as @mziraki said:

$l = Model::find(1);
dump($l);

and see in the output:

"location" => "\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00$@\x00\x00\x00\x00\x00\x00$@"

This should be something like:

"location" => MatanYadaev\EloquentSpatial\Objects\Point 
+latitude: 35.752816331305
+longitude: 51.4457340121
}

so for some reason the cast is not being applied when creating or retrieving a point field.

@Synchro
Copy link
Contributor

Synchro commented Apr 8, 2022

I have set breakpoints on the first line of every method in Point, Geometry, and GeometryCast classes, and none of them are called when doing find and dump operations! I have the feeling that something small but critical is missing.

Also note that this issue isn't anything to do with Nova, it just happens to show up there.

@Synchro
Copy link
Contributor

Synchro commented Apr 8, 2022

I made a fork of this repo and have written a test that shows the problem.

public function it_casts(): void
{
    /** @var TestPlace $testPlace */
    $testPlace = TestPlace::factory()->create([
        'point' => new Point(180, 0),
    ]);
    $testPlace2 = TestPlace::find($testPlace->id);
    dump($testPlace);
    dump($testPlace2);
}

The first dump() call produces this in the output:

"point" => Illuminate\Database\Query\Expression�]8;;file:///Users/marcus/Sites/laravel-eloquent-spatial/vendor/laravel/framework/src/Illuminate/Database/Query/Expression.php#L5�\^�]8;;�\ {#944
      #value: "POINT(0, 180)"
    }

the second one does this:

    "point" => b"\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00€f@"

So when the data is retrieved from the DB, it it not turned back into an object, and this is apparently not covered by the tests.

I spotted that GeometryCast::set returns a string, but Laravel docs say it should return an array. Unfortunately I don't see how to do that generically for all Geometry-based classes. I tried a simple patch, adding Point::set() returning

    return [
        'longitude' => $this->longitude,
        'latitude' => $this->latitude,
    ];

but that didn't do anything.

@Synchro
Copy link
Contributor

Synchro commented Apr 8, 2022

Oh, and here's a weird wrinkle; this passes!

$this->assertEquals($testPlace->point, $testPlace2->point);

@MatanYadaev
Copy link
Owner

@Synchro No worries, we'll fix it easily. Can you please share the branch that has failing tests with me? I just need a way to reproduce the bug so I'll know when it got fixed. Just send a PR with the failing tests.

@Synchro
Copy link
Contributor

Synchro commented Apr 8, 2022

Thanks. I've pushed a test, however, I can't figure out how to expose the failing behaviour in it. When you inspect the individual properties, they are cast correctly, but when you look at them with dump, they are not.

This assertion fails:

    $this->assertEquals($testPlace, $testPlace2);

but that seems to be more to do with the test factory, so I have modified the factory to set more of the default properties, but it still has a few differences.

@MatanYadaev
Copy link
Owner

MatanYadaev commented Apr 9, 2022

@Synchro I looked and played with your branch. It doesn't look like a real issue. Laravel stores the original value of the column point and casts it to Point object only on a direct access or on serialization.
When you dump the whole model it doesn't cast anything. This is how Laravel works.

$this->assertInstanceOf(Point::class, $testPlace->point);
$this->assertEquals($testPlace->toArray(), $testPlace2->toArray());
$this->assertEquals($testPlace->point->toArray(), $testPlace2->toArray()['point']);

BTW, the return type of GeometryCast::set is ok. You can return an array [$key => $geometry->toWkt()] but it will be the same as returning just $geometry->toWkt().

@Synchro
Copy link
Contributor

Synchro commented Apr 9, 2022

Well, the problem must be somewhere else then. I don't think it's a Nova issue because nova is simply acting on the instance of my model that is passed to it. I've also seen this problem that may be related: In my model I have this constructor:

public function __construct(array $attributes = [])
{
    parent::__construct($attributes);
    if (is_null($this->location)) {
        $this->location = new Point(51.5032973, -0.1195537);
    }
}

If I do a $l = Location::find(1);, that is_null condition is matched (the DB field is also not null), and I end up with that default value instead of the one from the DB, though all other values are set from the DB. If I don't do that check, I end up with a WKB string instead. I don't know why the cast is not being applied. Is there some other way that it could be accessed that's not triggering the cast? Is there some way I can force it to be applied?

@MatanYadaev
Copy link
Owner

@Synchro Can you reproduce the issue in a non-Nova environment? Can you try to write your own cast? Try removing 'location' => PointCast::class and add a method called location on your model like that: https://laravel.com/docs/9.x/eloquent-mutators#defining-a-mutator

@Synchro
Copy link
Contributor

Synchro commented Apr 14, 2022

I've invited you to a private repo for this, including Nova. It shows the casting problem described here, but also (incidentally) demonstrates the issue with non-null geo fields (#32) if you try to create a new Place, which it seems I have not solved after all. I also reported this in the Nova issues project to try and attack this problem from both ends.

@Synchro
Copy link
Contributor

Synchro commented Apr 22, 2022

Hi @MatanYadaev, did you get a chance to look at the project I set up?

@MatanYadaev
Copy link
Owner

MatanYadaev commented Apr 22, 2022

@Synchro Please create a new project from scratch that reproduce the issue without having any dependencies like Nova.
I tried installing your project but had issues with NovaApplicationServiceProvider and localhost:8000/nova responds in 404. I don't have time to debug the integration between my package and other packages.

@Synchro
Copy link
Contributor

Synchro commented Apr 22, 2022

I've done that.

Note that doing the equivalent thing with the Grimzy spatial library works, however, it lacks support for L9, and I prefer your API.

@MatanYadaev
Copy link
Owner

@Synchro

  1. About comparing models, I told you about it, you shouldn't compare model instances, you should only compare serialized/JSON versions. This is how casts work in Laravel. Support Laravel Nova #28 (comment)
  2. Currently it's impossible to set default values to spatial columns in the Model::__construct method. You should find a different way to set default values. You can try using the creating model event: https://laravel.com/docs/9.x/eloquent#events

@pavloniym
Copy link

@Synchro , @MatanYadaev

Same problem.
My fix:

class GeoobjectGeometry extends AbstractEloquentModel {

    // Casts
    protected $casts = [
        'center' => Point::class,
        'coordinates' => Geometry::class,
    ];
    
    // .....

    public function getRawOriginal($key = null, $default = null)
    {
        foreach (['center', 'coordinates'] as $cast) {
            $this->original[$cast] = Geometry::fromWkb(Arr::get($this->original, $cast, $default));
        }

        return Arr::get($this->original, $key, $default);
    }

}

@mostafaznv
Copy link

FYI:
I published a nova field based on this package.

https://github.com/mostafaznv/nova-map-field

@mostafaznv
Copy link

mostafaznv commented Jun 25, 2022

error: Unable to encode attribute [original] for model [Laravel\Nova\Actions\ActionEvent] to JSON: Malformed UTF-8 characters, possibly incorrectly encoded.

Hi everyone.
I think all these Nova issues are related to how this package is dealing with getChanges, getDirty and getRawOriginal.

Just look at this code:

Model:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use MatanYadaev\EloquentSpatial\Objects\Polygon;

class Area extends Model
{
    protected $casts = [
        'location' => Polygon::class,
    ];
}

Test:
Please run this code multiple times with that two datasets ($coordinates)

# retrieve a record
$area = Area::query()->latest()->first();

# datasets
$coordinates = [['4.3876602', '51.9096641'], ['4.3869092', '51.9085389'], ['4.387038', '51.9082079'], ['4.3946554', '51.906434'], ['4.3953421', '51.906434'], ['4.3963506', '51.9066856'], ['4.3959214', '51.9078505'], ['4.393883', '51.9082212'], ['4.391072', '51.9085389'], ['4.3909647', '51.9086845'], ['4.3910505', '51.9092405'], ['4.3894627', '51.9093331'], ['4.3876602', '51.9096641']];
$coordinates = [['4.3874886', '51.9096893'], ['4.3836691', '51.9100599'], ['4.3832185', '51.9090803'], ['4.3859865', '51.908392'], ['4.3864801', '51.9083787'], ['4.3866946', '51.9084846'], ['4.3874886', '51.9096893']];

# prepare a polygon using the chosen dataset
$points = [];

foreach ($coordinates as $coordinate) {
    $points[] = new Point($coordinate[0], $coordinate[1]);
}

$area->location = new Polygon([
    new LineString($points)
]);


# results
dump('raw original: ', $area->getRawOriginal('location'));
dump('dirty: ', $area->getDirty());

$area->save();
        
dump('changes: ', $area->getChanges());

dd();

You will see these results:

Get Dirty
location is always dirty. doesn't matter if we changed the dataset or not. it's always dirty

Get Changes
location is always there. Laravel thinks it changes every time we save this record.

⚠️ Get Raw Original
It returns binary (and it's not a wrong behavior).
But when Nova wants to log changes to database, it can't cast that binary to string and returns Malformed UTF-8 characters exception. so fixed it by this trait (but I have not done anything about getChanges and getDirty yet)
I know spatial fields are stored to database with that format, but binary is not usable in application layer and if we cast it to json (or something else) by default, it will benefit us in these situations.

getRawOriginal, getDirty and getChanges are not nova things. these methods are commonly used by developers in almost all laravel-based services.


More Information:
This is stack trace of the error and nova's source code:

[2022-06-25 07:11:09] local.ERROR: Unable to encode attribute [original] for model [Laravel\Nova\Actions\ActionEvent] to JSON: Malformed UTF-8 characters, possibly incorrectly encoded. {"userId":1,"exception":"[object] (Illuminate\\Database\\Eloquent\\JsonEncodingException(code: 0): Unable to encode attribute [original] for model [Laravel\\Nova\\Actions\\ActionEvent] to JSON: Malformed UTF-8 characters, possibly incorrectly encoded. at /path/vendor/laravel/framework/src/Illuminate/Database/Eloquent/JsonEncodingException.php:47)

Screen Shot 1401-04-04 at 2 55 50 PM

@MatanYadaev
Copy link
Owner

@mostafaznv Great, thanks for the research. Can you please send a PR fixing it?

@MatanYadaev MatanYadaev reopened this Jun 26, 2022
@mostafaznv
Copy link

@mostafaznv Great, thanks for the research. Can you please send a PR fixing it?

Yes, I will work on it soon.

@MatanYadaev
Copy link
Owner

Should be fixed in version 2.

But, there's a catch, Laravel doesn't compare between two different geometry instances. They might have the same values, but the instances are different, so Laravel see it as dirty.

You can fix it by adding this snippet to your model:

public function originalIsEquivalent($key)
    {
        if (! array_key_exists($key, $this->original)) {
            return false;
        }

        if (parent::originalIsEquivalent($key)) {
            return true;
        }

        if($this->isClassCastable($key) && is_subclass_of($this->getCasts()[$key], Geometry::class)) {
            $originalGeometry = $this->getOriginal($key);
            $attributeWkbOrWkt = Arr::get($this->attributes, $key);

            return $this->castAttribute($key, $attributeWkbOrWkt) == $originalGeometry;
        }

        return false;
    }

Please check if it works for you. If so, I'll try to add it to the core of this package.

@mostafaznv
Copy link

Hi @MatanYadaev

Thanks for this update.
It seems both getDirty and getChanges methods are working fine and creating resources in Nova admin panel also works fine but I still have an error during updating resources in Nova.

[2022-07-08 08:32:30] local.ERROR: Expected MatanYadaev\EloquentSpatial\GeometryCast, string given. {"userId":1,"exception":"[object] (InvalidArgumentException(code: 0): Expected MatanYadaev\\EloquentSpatial\\GeometryCast, string given. at /project/vendor/matanyadaev/laravel-eloquent-spatial/src/GeometryCast.php:66)
[stacktrace]
#0 /project/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php(1087): MatanYadaev\\EloquentSpatial\\GeometryCast->set(Object(App\\Models\\Location), 'area', '\\x00\\x00\\x00\\x00\\x01\\x03\\x00\\x00\\x00\\x01\\x00\\x00\\x00\
\\x00...', Array)
#1 /project/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php(932): Illuminate\\Database\\Eloquent\\Model->setClassCastableAttribute('area', '\\x00\\x00\\x00\\x00\\x01\\x03\\x00\\x00\\x00\\x01\\x00\\x00\\x00\
\\x00...')
#2 /project/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php(431): Illuminate\\Database\\Eloquent\\Model->setAttribute('area', '\\x00\\x00\\x00\\x00\\x01\\x03\\x00\\x00\\x00\\x01\\x00\\x00\\x00\
\\x00...')
#3 /project/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php(452): Illuminate\\Database\\Eloquent\\Model->fill(Array)
#4 /project/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/GuardsAttributes.php(155): Illuminate\\Database\\Eloquent\\Model->Illuminate\\Database\\Eloquent\\{closure}()
#5 /project/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php(453): Illuminate\\Database\\Eloquent\\Model::unguarded(Object(Closure))
#6 /project/vendor/laravel/nova/src/Actions/ActionEvent.php(115): Illuminate\\Database\\Eloquent\\Model->forceFill(Array)
#7 /project/vendor/laravel/nova/src/Http/Controllers/ResourceUpdateController.php(50): Laravel\\Nova\\Actions\\ActionEvent::forResourceUpdate(Object(App\\Models\\User), Object(App\\Models\\Location))
#8 [internal function]: Laravel\\Nova\\Http\\Controllers\\ResourceUpdateController->Laravel\\Nova\\Http\\Controllers\\{closure}(Object(Laravel\\Nova\\Actions\\ActionEvent))
#9 /project/vendor/laravel/nova/src/Concerns/InteractsWithActionEvent.php(38): call_user_func(Object(Closure), Object(Laravel\\Nova\\Actions\\ActionEvent))
#10 /project/vendor/laravel/nova/src/Http/Controllers/ResourceUpdateController.php(52): Laravel\\Nova\\Nova::usingActionEvent(Object(Closure))
#11 /project/vendor/laravel/framework/src/Illuminate/Database/Concerns/ManagesTransactions.php(30): Laravel\\Nova\\Http\\Controllers\\ResourceUpdateController->Laravel\\Nova\\Http\\Controllers\\{closure}(Object(Illuminate\\Database\\MySqlConnection))
#12 /project/vendor/laravel/framework/src/Illuminate/Database/DatabaseManager.php(462): Illuminate\\Database\\Connection->transaction(Object(Closure))
#13 /project/vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php(337): Illuminate\\Database\\DatabaseManager->__call('transaction', Array)
#14 /project/vendor/laravel/nova/src/Http/Controllers/ResourceUpdateController.php(53): Illuminate\\Support\\Facades\\Facade::__callStatic('transaction', Array)
#15 /project/vendor/laravel/framework/src/Illuminate/Database/Concerns/ManagesTransactions.php(30): Laravel\\Nova\\Http\\Controllers\\ResourceUpdateController->Laravel\\Nova\\Http\\Controllers\\{closure}(Object(Illuminate\\Database\\MySqlConnection))
#16 /project/vendor/laravel/nova/src/Http/Controllers/ResourceUpdateController.php(64): Illuminate\\Database\\Connection->transaction(Object(Closure))
#17 /project/vendor/laravel/framework/src/Illuminate/Routing/Controller.php(54): Laravel\\Nova\\Http\\Controllers\\ResourceUpdateController->__invoke(Object(Laravel\\Nova\\Http\\Requests\\UpdateResourceRequest), 'locations', '12')
#18 /project/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(45): Illuminate\\Routing\\Controller->callAction('__invoke', Array)
#19 /project/vendor/laravel/framework/src/Illuminate/Routing/Route.php(268): Illuminate\\Routing\\ControllerDispatcher->dispatch(Object(Illuminate\\Routing\\Route), Object(Laravel\\Nova\\Http\\Controllers\\ResourceUpdateController), '__invoke')
#20 /project/vendor/laravel/framework/src/Illuminate/Routing/Route.php(211): Illuminate\\Routing\\Route->runController()
#21 /project/vendor/laravel/framework/src/Illuminate/Routing/Router.php(725): Illuminate\\Routing\\Route->run()
#22 /project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(141): Illuminate\\Routing\\Router->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))
#23 /project/vendor/laravel/nova/src/Http/Middleware/Authorize.php(18): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#24 /project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Laravel\\Nova\\Http\\Middleware\\Authorize->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#25 /project/vendor/laravel/nova/src/Http/Middleware/BootTools.php(20): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#26 /project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Laravel\\Nova\\Http\\Middleware\\BootTools->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#27 /project/vendor/laravel/nova/src/Http/Middleware/DispatchServingNovaEvent.php(24): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#28 /project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Laravel\\Nova\\Http\\Middleware\\DispatchServingNovaEvent->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#29 /project/vendor/inertiajs/inertia-laravel/src/Middleware.php(92): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#30 /project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Inertia\\Middleware->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#31 /project/vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php(50): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#32 /project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Routing\\Middleware\\SubstituteBindings->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#33 /project/vendor/laravel/framework/src/Illuminate/Auth/Middleware/Authenticate.php(44): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#34 /project/vendor/laravel/nova/src/Http/Middleware/Authenticate.php(31): Illuminate\\Auth\\Middleware\\Authenticate->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#35 /project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Laravel\\Nova\\Http\\Middleware\\Authenticate->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#36 /project/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php(78): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#37 /project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\VerifyCsrfToken->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#38 /project/vendor/laravel/framework/src/Illuminate/View/Middleware/ShareErrorsFromSession.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#39 /project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\View\\Middleware\\ShareErrorsFromSession->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#40 /project/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(121): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#41 /project/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(64): Illuminate\\Session\\Middleware\\StartSession->handleStatefulRequest(Object(Illuminate\\Http\\Request), Object(Illuminate\\Session\\Store), Object(Closure))
#42 /project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Session\\Middleware\\StartSession->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#43 /project/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/AddQueuedCookiesToResponse.php(37): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#44 /project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Cookie\\Middleware\\AddQueuedCookiesToResponse->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#45 /project/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/EncryptCookies.php(67): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#46 /project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Cookie\\Middleware\\EncryptCookies->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#47 /project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(116): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#48 /project/vendor/laravel/framework/src/Illuminate/Routing/Router.php(726): Illuminate\\Pipeline\\Pipeline->then(Object(Closure))
#49 /project/vendor/laravel/framework/src/Illuminate/Routing/Router.php(703): Illuminate\\Routing\\Router->runRouteWithinStack(Object(Illuminate\\Routing\\Route), Object(Illuminate\\Http\\Request))
#50 /project/vendor/laravel/framework/src/Illuminate/Routing/Router.php(667): Illuminate\\Routing\\Router->runRoute(Object(Illuminate\\Http\\Request), Object(Illuminate\\Routing\\Route))
#51 /project/vendor/laravel/framework/src/Illuminate/Routing/Router.php(656): Illuminate\\Routing\\Router->dispatchToRoute(Object(Illuminate\\Http\\Request))
#52 /project/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(167): Illuminate\\Routing\\Router->dispatch(Object(Illuminate\\Http\\Request))
#53 /project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(141): Illuminate\\Foundation\\Http\\Kernel->Illuminate\\Foundation\\Http\\{closure}(Object(Illuminate\\Http\\Request))
#54 /project/vendor/laravel/nova/src/Http/Middleware/ServeNova.php(23): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#55 /project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Laravel\\Nova\\Http\\Middleware\\ServeNova->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#56 /project/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#57 /project/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ConvertEmptyStringsToNull.php(31): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#58 /project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#59 /project/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#60 /project/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php(40): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#61 /project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\TrimStrings->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#62 /project/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ValidatePostSize.php(27): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#63 /project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#64 /project/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/PreventRequestsDuringMaintenance.php(86): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#65 /project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#66 /project/vendor/laravel/framework/src/Illuminate/Http/Middleware/HandleCors.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#67 /project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Http\\Middleware\\HandleCors->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#68 /project/vendor/laravel/framework/src/Illuminate/Http/Middleware/TrustProxies.php(39): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#69 /project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Http\\Middleware\\TrustProxies->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#70 /project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(116): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#71 /project/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(142): Illuminate\\Pipeline\\Pipeline->then(Object(Closure))
#72 /project/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(111): Illuminate\\Foundation\\Http\\Kernel->sendRequestThroughRouter(Object(Illuminate\\Http\\Request))
#73 /project/public/index.php(52): Illuminate\\Foundation\\Http\\Kernel->handle(Object(Illuminate\\Http\\Request))
#74 /project/server.php(21): require_once('/project...')
#75 {main}
"} 

@MatanYadaev
Copy link
Owner

@mostafaznv Can you send a PR with a failing test?

@mostafaznv
Copy link

mostafaznv commented Jul 14, 2022

Should be fixed in version 2.

But, there's a catch, Laravel doesn't compare between two different geometry instances. They might have the same values, but the instances are different, so Laravel see it as dirty.

You can fix it by adding this snippet to your model:

public function originalIsEquivalent($key)
    {
        if (! array_key_exists($key, $this->original)) {
            return false;
        }

        if (parent::originalIsEquivalent($key)) {
            return true;
        }

        if($this->isClassCastable($key) && is_subclass_of($this->getCasts()[$key], Geometry::class)) {
            $originalGeometry = $this->getOriginal($key);
            $attributeWkbOrWkt = Arr::get($this->attributes, $key);

            return $this->castAttribute($key, $attributeWkbOrWkt) == $originalGeometry;
        }

        return false;
    }

Please check if it works for you. If so, I'll try to add it to the core of this package.

I can confirm this workaround works nice and I'm excited to use it in new versions 🚀
Can you give me any estimation about release date?

@MatanYadaev
Copy link
Owner

@mostafaznv Version 2 is already released. Please let me know how it works with Nova.

@mostafaznv
Copy link

@mostafaznv Version 2 is already released. Please let me know how it works with Nova.

Last week when I tried v2.0.0, I had some issues on Nova (I don't know if it was my own mistake or it was because of your package) but today I tried v2.0.1 and everything just worked fine.

(of course I added that originalIsEquivalent function to my model)

@mostafaznv
Copy link

originalIsEquivalent

Any updates about originalIsEquivalent?

@MatanYadaev
Copy link
Owner

@mostafaznv No. I don't think I will push this into the package's source code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants