1

I have worked with most of existing PHP frameworks, but this is the first time I am working with Nette and unfortunately I can't make something which sounded like an easy task to work. How SHOULD it work:

  • user sends a POST request to an API endpoint created using tomaj/nette-api
  • backend validates the sent data and saves them in the database using nextras/orm
  • backend sends a response

How it WORKS:

  • user sends a POST request
  • backend returns the error Nextras\Orm\InvalidStateException: MetadataStorage::get() called too early. You have to instantiate your model first.

After debugging and googling I found out that the problem is that I am trying to use the entity before I instantiated the model. The model object I am sending to the api handler class through DI (at least that's what I thought I'm doing), but when I try to var_dump it, I get the error message Typed property ... must not be accessed before initialization

This is how my configuration looks like for the API endpoints

services:
    - App\Core\RouterFactory::createRouter
    apiOutputConfigurator: Tomaj\NetteApi\Output\Configurator\DebuggerConfigurator
    apiErrorHandler: Tomaj\NetteApi\Error\DefaultErrorHandler

    # define apiKey in local.neon
    staticTokenRepository: Tomaj\NetteApi\Misc\StaticTokenRepository([%apiKey%: '*'])
    - Tomaj\NetteApi\Link\ApiLink
    - Tomaj\NetteApi\Misc\IpDetector
    apiDecider:
        factory: Tomaj\NetteApi\ApiDecider
        setup:
            - addApi(Tomaj\NetteApi\EndpointIdentifier('GET', 1, 'get-endpoint'), \App\api\Handlers\GetEndpointHandler(), Tomaj\NetteApi\Authorization\NoAuthorization())
            - addApi(Tomaj\NetteApi\EndpointIdentifier('POST', 1, 'post-endpoint'), \App\api\Handlers\PostEndpointHandler(), \Tomaj\NetteApi\Authorization\QueryApiKeyAuthentication('api_key', @staticTokenRepository))

This is how my configuration looks like for the ORM:

extensions:
    nextras.dbal: Nextras\Dbal\Bridges\NetteDI\DbalExtension
    nextras.orm: Nextras\Orm\Bridges\NetteDI\OrmExtension

nextras.orm:
    model: App\Model\Orm

services:
    - \App\Model\Repository\MyRepository(\App\Model\Mapper\MyMapper())

The names of the classes are of course censored for privacy reasons but they are not that important.

This is my entity

/**
 * @property int           $id         {primary}
 * LIST OF ALL OTHER PROPERTIES
 */
class MyEntity extends Entity
{

}

This is my mapper

/**
 * @extends DbalMapper<MyEntity>
 */
class MyMapper extends DbalMapper
{

}

And this is the repository class

/**
 * @extends Repository<MyEntity>
 */
class MyRepository extends Repository
{
    /**
     * @inheritDoc
     */
    public static function getEntityClassNames(): array
    {
        return [MyEntity::class];
    }
}

And last but not least the model class

/**
 * Model
 *
 * @property-read MyRepository $things
 */
class Orm extends Model
{

}

I tried to inject the model object to the handler class simply like this (as I've seen in the demo)

#[Inject]
    private Orm $orm;

but as the error messages say it's not working. What did I forget? As I wrote above this is the first time I am doing something with Nette so I'm pretty sure it's something what an experienced Nette dev would find immediately. I have also studied the demo which can be found here : https://github.com/nextras/orm-demo-nette but it woudn't help, I think I have everything done the same way - the only change being is they are injecting it to a presenter and I need to get it working in a Api handler. Thanks for any help.

EDIT: If I try to manually send the ORM in the constructor of the handler class, I am putting it to the config here (hopefully correctly)

apiDecider:
        factory: Tomaj\NetteApi\ApiDecider
        setup:
            - addApi(Tomaj\NetteApi\EndpointIdentifier('GET', 1, 'get-endpoint'), \App\api\Handlers\GetEndpointHandler(App\Model\Orm()), Tomaj\NetteApi\Authorization\NoAuthorization())
            - addApi(Tomaj\NetteApi\EndpointIdentifier('POST', 1, 'post-endpoint'), \App\api\Handlers\PostEndpointHandler(App\Model\Orm()), \Tomaj\NetteApi\Authorization\QueryApiKeyAuthentication('api_key', @staticTokenRepository))

I am getting a new error Service 'apiDecider' (type of Tomaj\NetteApi\ApiDecider): Parameter $configuration in Nextras\Orm\Model\Model::__construct() has no class type or default value, so its value must be specified.

2
  • If possible try defining the private property as nullable. private ?Orm $orm = null; Commented May 20 at 17:15
  • I took it that the error is being caused by the $orm property being accessed before initialization. Commented May 20 at 17:21

2 Answers 2

2

The error says it clearly - you haven't touched ORM classes (repository or model) yet, and you're constructing the instance already.

You're code doesn't show the place where you're constructing the entity, but the idea to inject there ORM is the right thing. Generally, Nette utilizes public constructor injection, so I'd recommend that. Alternatively, share what the error is with injection.

Sign up to request clarification or add additional context in comments.

6 Comments

Thank you for your response, you're the creator if I'm not mistaken? When I add the constructor of the model class to the constructor of the Api Handler class in the config file, I am getting a new error - I have added the edited config and the new error to the original post. The new error is Service 'apiDecider' (type of Tomaj\NetteApi\ApiDecider): Parameter $configuration in Nextras\Orm\Model\Model::__construct() has no class type or default value, so its value must be specified.
@pittrek it seems the key is how Tomaj\NetteApi\ApiDecider handles dependency injection. So I guess you should look into this framework. Alternatively, you may initialize Nextras Orm on app start - e.g. somewhere in bootstrap. What version of Orm and Nette/DI do you use?
we're using Nette/DI 3.2.4 and ORM 5.0. How can I register ORM in bootstrap, that sounds like something worth trying. And thanks of course
@pittrek have you enabled lazy DI? Since if not, Orm tries to start itself asap - but it is broken with lazy Nette/DI. I plan to fix that: github.com/nextras/orm/issues/743
hm, not sure what "lazy DI" is, but if you're talking about what I think, the tomaj/nette-api uses lazy loading, so can that be a problem?
|
2

So ... this is embarrassing but I found the error and it was on my side. For whatever reason creating an empty web application with composer generates Bootstrap.php which reads services.neon, but cloning our repo generates a Bootstrap.php where all config files have singular names, meaning service.neon . For some reason. In other words I was writing my configuration in a file which Bootstrap did NOT load. After renaming it properly, everything suddenly works as expected, what a surprise. Thanks for everybody's time, I'm going to silently rethink my life :)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.