laravel-activitylog icon indicating copy to clipboard operation
laravel-activitylog copied to clipboard

Composite primary key "Illegal offset type error"

Open seifti opened this issue 2 years ago • 3 comments

Describe the bug When saving a new row for a specific model with a composite primary key, it throws the following error:

[2022-07-17 09:26:26] local.ERROR: Illegal offset type {"userId":3,"exception":"[object] (TypeError(code: 0): Illegal offset type at /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php:1134)
[stacktrace]
#0 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php(1122): Illuminate\\Database\\Eloquent\\Model->getKeyForSelectQuery()
#1 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php(1561): Illuminate\\Database\\Eloquent\\Model->setKeysForSelectQuery()
#2 /var/www/gdpr/vendor/spatie/laravel-activitylog/src/Traits/LogsActivity.php(276): Illuminate\\Database\\Eloquent\\Model->fresh()
#3 /var/www/gdpr/vendor/spatie/laravel-activitylog/src/Traits/LogsActivity.php(54): App\\CompanyRightRequestDefaultAssignee->attributeValuesToBeLogged()

To Reproduce My model looks like below, if I comment the activity log part, it works fine. On my other models, where I don't use composite keys, it works great:

<?php
namespace App;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Spatie\Activitylog\Traits\LogsActivity;
use Spatie\Activitylog\LogOptions;
use App\Traits\HasCompositePrimaryKeyTrait;

class CompanyRightRequestDefaultAssignee extends Model
{
    use HasFactory, HasCompositePrimaryKeyTrait;

    public $incrementing = false;
    protected $primaryKey = ['company_id', 'user_id', 'group_code', 'type_code'];

    use LogsActivity;

    public function getActivitylogOptions(): LogOptions
    {
        return LogOptions::defaults()->logOnly(['*'])->logOnlyDirty()->dontLogIfAttributesChangedOnly(['updated_at'])->dontSubmitEmptyLogs();    
    }
}

Expected behavior That it behaves as with other models

Versions (please complete the following information)

  • PHP: 8.1
  • Database: 8.0
  • Laravel: 9.0
  • Package: "spatie/laravel-activitylog": "^4.3"

Stack Trace

[2022-07-17 09:26:26] local.ERROR: Illegal offset type {"userId":3,"exception":"[object] (TypeError(code: 0): Illegal offset type at /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php:1134)
[stacktrace]
#0 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php(1122): Illuminate\\Database\\Eloquent\\Model->getKeyForSelectQuery()
#1 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php(1561): Illuminate\\Database\\Eloquent\\Model->setKeysForSelectQuery()
#2 /var/www/gdpr/vendor/spatie/laravel-activitylog/src/Traits/LogsActivity.php(276): Illuminate\\Database\\Eloquent\\Model->fresh()
#3 /var/www/gdpr/vendor/spatie/laravel-activitylog/src/Traits/LogsActivity.php(54): App\\CompanyRightRequestDefaultAssignee->attributeValuesToBeLogged()
#4 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php(421): App\\CompanyRightRequestDefaultAssignee::Spatie\\Activitylog\\Traits\\{closure}()
#5 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php(249): Illuminate\\Events\\Dispatcher->Illuminate\\Events\\{closure}()
#6 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/HasEvents.php(189): Illuminate\\Events\\Dispatcher->dispatch()
#7 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php(1108): Illuminate\\Database\\Eloquent\\Model->fireModelEvent()
#8 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php(1019): Illuminate\\Database\\Eloquent\\Model->performUpdate()
#9 /var/www/gdpr/app/Http/Controllers/CompanyController.php(712): Illuminate\\Database\\Eloquent\\Model->save()
#10 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Routing/Controller.php(54): App\\Http\\Controllers\\CompanyController->postRightRequestDefaultAssignee()
#11 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(45): Illuminate\\Routing\\Controller->callAction()
#12 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Routing/Route.php(268): Illuminate\\Routing\\ControllerDispatcher->dispatch()
#13 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Routing/Route.php(211): Illuminate\\Routing\\Route->runController()
#14 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Routing/Router.php(725): Illuminate\\Routing\\Route->run()
#15 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(141): Illuminate\\Routing\\Router->Illuminate\\Routing\\{closure}()
#16 /var/www/gdpr/app/Http/Middleware/HasRights.php(54): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#17 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): App\\Http\\Middleware\\HasRights->handle()
#18 /var/www/gdpr/app/Http/Middleware/Locale.php(33): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#19 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): App\\Http\\Middleware\\Locale->handle()
#20 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php(50): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#21 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Routing\\Middleware\\SubstituteBindings->handle()
#22 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/AddQueuedCookiesToResponse.php(37): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#23 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Cookie\\Middleware\\AddQueuedCookiesToResponse->handle()
#24 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Routing/Middleware/ThrottleRequests.php(126): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#25 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Routing/Middleware/ThrottleRequests.php(62): Illuminate\\Routing\\Middleware\\ThrottleRequests->handleRequest()
#26 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Routing\\Middleware\\ThrottleRequests->handle()
#27 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Auth/Middleware/Authenticate.php(44): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#28 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Auth\\Middleware\\Authenticate->handle()
#29 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/View/Middleware/ShareErrorsFromSession.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#30 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\View\\Middleware\\ShareErrorsFromSession->handle()
#31 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(121): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#32 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(64): Illuminate\\Session\\Middleware\\StartSession->handleStatefulRequest()
#33 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Session\\Middleware\\StartSession->handle()
#34 /var/www/gdpr/vendor/laravel/sanctum/src/Http/Middleware/EnsureFrontendRequestsAreStateful.php(33): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#35 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(141): Laravel\\Sanctum\\Http\\Middleware\\EnsureFrontendRequestsAreStateful->Laravel\\Sanctum\\Http\\Middleware\\{closure}()
#36 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php(78): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#37 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\VerifyCsrfToken->handle()
#38 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(121): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#39 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(64): Illuminate\\Session\\Middleware\\StartSession->handleStatefulRequest()
#40 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Session\\Middleware\\StartSession->handle()
#41 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/AddQueuedCookiesToResponse.php(37): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#42 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Cookie\\Middleware\\AddQueuedCookiesToResponse->handle()
#43 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/EncryptCookies.php(67): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#44 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Cookie\\Middleware\\EncryptCookies->handle()
#45 /var/www/gdpr/vendor/laravel/sanctum/src/Http/Middleware/EnsureFrontendRequestsAreStateful.php(26): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#46 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(162): Laravel\\Sanctum\\Http\\Middleware\\EnsureFrontendRequestsAreStateful->Laravel\\Sanctum\\Http\\Middleware\\{closure}()
#47 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(116): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#48 /var/www/gdpr/vendor/laravel/sanctum/src/Http/Middleware/EnsureFrontendRequestsAreStateful.php(34): Illuminate\\Pipeline\\Pipeline->then()
#49 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Laravel\\Sanctum\\Http\\Middleware\\EnsureFrontendRequestsAreStateful->handle()
#50 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(116): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#51 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Routing/Router.php(726): Illuminate\\Pipeline\\Pipeline->then()
#52 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Routing/Router.php(703): Illuminate\\Routing\\Router->runRouteWithinStack()
#53 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Routing/Router.php(667): Illuminate\\Routing\\Router->runRoute()
#54 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Routing/Router.php(656): Illuminate\\Routing\\Router->dispatchToRoute()
#55 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(167): Illuminate\\Routing\\Router->dispatch()
#56 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(141): Illuminate\\Foundation\\Http\\Kernel->Illuminate\\Foundation\\Http\\{closure}()
#57 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#58 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ConvertEmptyStringsToNull.php(31): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle()
#59 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull->handle()
#60 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#61 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php(40): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle()
#62 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\TrimStrings->handle()
#63 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ValidatePostSize.php(27): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#64 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize->handle()
#65 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/PreventRequestsDuringMaintenance.php(86): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#66 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance->handle()
#67 /var/www/gdpr/vendor/fruitcake/laravel-cors/src/HandleCors.php(52): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#68 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Fruitcake\\Cors\\HandleCors->handle()
#69 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Http/Middleware/TrustProxies.php(39): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#70 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\\Http\\Middleware\\TrustProxies->handle()
#71 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(116): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#72 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(142): Illuminate\\Pipeline\\Pipeline->then()
#73 /var/www/gdpr/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(111): Illuminate\\Foundation\\Http\\Kernel->sendRequestThroughRouter()
#74 /var/www/gdpr/public/index.php(55): Illuminate\\Foundation\\Http\\Kernel->handle()
#75 {main}
"} 

seifti avatar Jul 17 '22 09:07 seifti

Hello @seifti

Acutally the issue is not related to the package. This issue caused because Eloquent doens't support composite primary keys. See https://github.com/laravel/framework/issues/5355.

Elsenosy avatar Jul 30 '22 23:07 Elsenosy

I have composite primary keys in many models, I'm just using the following trait @Elsenosy

<?php
namespace App\Traits;
use Illuminate\Database\Eloquent\Builder;

trait HasCompositePrimaryKeyTrait{

  //Get the value indicating whether the IDs are incrementing.
  public function getIncrementing(){
    return false;
  }


  //Set the keys for a save update query.
  protected function setKeysForSaveQuery($query){ //edit Builder $query to $query

    foreach ($this->getKeyName() as $key) {
      // UPDATE: Added isset() per devflow's comment.
      if (isset($this->$key)){
        $query->where($key, '=', $this->$key);
      }else
      throw new Exception(__METHOD__ . 'Missing part of the primary key: ' . $key);
    }
    return $query;
  }


  protected function getKeyForSaveQuery($keyName = null)
  {

    if(is_null($keyName)){
      $keyName = $this->getKeyName();
    }

    if (isset($this->original[$keyName])) {
      return $this->original[$keyName];
    }

    return $this->getAttribute($keyName);
  }



  public static function find($ids, $columns = ['*']){
   $me = new self;
   $query = $me->newQuery();

   foreach ($me->getKeyName() as $key) {
     $query->where($key, '=', $ids[$key]);
   }

   return $query->first($columns);
 }
}

seifti avatar Jul 31 '22 01:07 seifti

@seifti

Yes, the trait HasCompositePrimaryKeyTrait works fine with saving and retrieving data, but what about the model fresh method? did you test it? ActivityLogger use the fresh method to get logging data.

The second thing you have to worry about that the ActivityLogger model has morph relationship for the causer and subject models. The subject model in your case is the 'CompanyRightRequestDefaultAssignee' model which has composite keys, so when using morph relationship for the subject, a specific one -and only one- key is used to determine the relationship, in our case you are using array of keys in CompanyRightRequestDefaultAssignee model and here where the problem lies. So back again to our main point: Eloquent doesn't support composite keys.

Regards.

Elsenosy avatar Jul 31 '22 07:07 Elsenosy

Like said it's not a bug with the package but your trait and composite keys at all.

Gummibeer avatar Aug 28 '22 08:08 Gummibeer