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

Error: Missing contentTable field in Matrix field when calling Craft::$app->getUsers()->getUserByUsernameOrEmail() #3389

Closed
nickfreedom opened this issue Oct 19, 2018 · 4 comments

Comments

@nickfreedom
Copy link

nickfreedom commented Oct 19, 2018

Description

I'm seeing the following error when attempting to pull a user in a plugin, using getUserFromUsernameOrEmail:

yii\base\UnknownPropertyException: Setting unknown property: craft\fields\Matrix::contentTable in /usr/share/nginx/vendor/yiisoft/yii2/base/Component.php:209

I confirmed there is a contentTable property in the settings column for the Matrix field in question, so it appears there is an issue with setting that property for Matrix fields when building a list of fields as part of the getUserFromUsernameOrEmail call chain.

Full stack trace can be found below.

Steps to reproduce

  1. Create a Matrix field.
  2. Create a plugin action that calls Craft::$app->getUsers()->getUserByUsernameOrEmail()
  3. Error is thrown.
  4. Remove Matrix field.
  5. Error goes away.

Additional info

  • Craft version: 3.0.27.1 PRO
  • PHP version: 7.2
  • Database driver & version: MySQL 5.7.23

Stack Trace:

yii\base\UnknownPropertyException: Setting unknown property: craft\fields\Matrix::contentTable in /usr/share/nginx/vendor/yiisoft/yii2/base/Component.php:209
Stack trace:
#0 /usr/share/nginx/vendor/yiisoft/yii2/BaseYii.php(546): yii\base\Component->__set('contentTable', '{{%matrixconten...')
#1 /usr/share/nginx/vendor/yiisoft/yii2/base/BaseObject.php(107): yii\BaseYii::configure(Object(craft\fields\Matrix), Array)
#2 /usr/share/craft-iot-poc/vendor/craftcms/cms/src/helpers/Component.php(80): yii\base\BaseObject->__construct(Array)
#3 /usr/share/nginx/vendor/craftcms/cms/src/services/Fields.php(506): craft\helpers\Component::createComponent(Array, 'craft\\base\\Fiel...')
#4 /usr/share/nginx/vendor/craftcms/cms/src/services/Fields.php(549): craft\services\Fields->createField(Array)
#5 /usr/share/craft-iot-poc/vendor/craftcms/cms/src/elements/db/ElementQuery.php(1499): craft\services\Fields->getAllFields()
#6 /usr/share/craft-iot-poc/vendor/craftcms/cms/src/elements/db/ElementQuery.php(1051): craft\elements\db\ElementQuery->customFields()
#7 /usr/share/nginx/vendor/yiisoft/yii2/db/QueryBuilder.php(227): craft\elements\db\ElementQuery->prepare(Object(craft\db\mysql\QueryBuilder))
#8 /usr/share/nginx/vendor/yiisoft/yii2/db/Query.php(146): yii\db\QueryBuilder->build(Object(craft\elements\db\UserQuery))
#9 /usr/share/nginx/vendor/yiisoft/yii2/db/Query.php(274): yii\db\Query->createCommand(Object(craft\db\Connection))
#10 /usr/share/nginx/vendor/craftcms/cms/src/db/Query.php(177): yii\db\Query->one(NULL)
#11 /usr/share/craft-iot-poc/vendor/craftcms/cms/src/elements/db/ElementQuery.php(1183): craft\db\Query->one(NULL)
#12 /usr/share/craft-iot-poc/vendor/craftcms/cms/src/services/Users.php(175): craft\elements\db\ElementQuery->one()
#13 /usr/share/craft-iot-poc/src/controllers/ApiController.php(497): craft\services\Users->getUserByUsernameOrEmail('testing')
#14 /usr/share/craft-iot-poc/src/controllers/ApiController.php(115): nickleguillou\craftiotpoc\controllers\ApiController->getUserFromRequestParams()
#15 [internal function]: nickleguillou\craftiotpoc\controllers\ApiController->actionGetApiKeyForUser()
#16 /usr/share/craft-iot-poc/vendor/yiisoft/yii2/base/InlineAction.php(57): call_user_func_array(Array, Array)
#17 /usr/share/craft-iot-poc/vendor/yiisoft/yii2/base/Controller.php(157): yii\base\InlineAction->runWithParams(Array)
#18 /usr/share/craft-iot-poc/vendor/craftcms/cms/src/web/Controller.php(103): yii\base\Controller->runAction('get-api-key-for...', Array)
#19 /usr/share/nginx/vendor/yiisoft/yii2/base/Module.php(528): craft\web\Controller->runAction('get-api-key-for...', Array)
#20 /usr/share/nginx/vendor/craftcms/cms/src/web/Application.php(282): yii\base\Module->runAction('craft-iot-poc/a...', Array)
#21 /usr/share/nginx/vendor/craftcms/cms/src/web/Application.php(542): craft\web\Application->runAction('craft-iot-poc/a...', Array)
#22 /usr/share/nginx/vendor/craftcms/cms/src/web/Application.php(266): craft\web\Application->_processActionRequest(Object(craft\web\Request))
#23 /usr/share/nginx/vendor/yiisoft/yii2/base/Application.php(386): craft\web\Application->handleRequest(Object(craft\web\Request))
#24 /usr/share/nginx/web/index.php(21): yii\base\Application->run()
#25 {main}
@nickfreedom
Copy link
Author

Copy/Paste of "pretty" version of stack trace (to see function call arguments):

Unknown Property – yii\base\UnknownPropertyException

Setting unknown property: craft\fields\Matrix::contentTable
1. in /usr/share/nginx/vendor/yiisoft/yii2/base/Component.php at line 209
200201202203204205206207208209210211212213214215216217218                                    $behavior->$name = $value;
                return;
            }
        }
 
        if (method_exists($this, 'get' . $name)) {
            throw new InvalidCallException('Setting read-only property: ' . get_class($this) . '::' . $name);
        }
 
        throw new UnknownPropertyException('Setting unknown property: ' . get_class($this) . '::' . $name);
    }
 
    /**
     * Checks if a property is set, i.e. defined and not null.
     *
     * This method will check in the following order and act accordingly:
     *
     *  - a property defined by a setter: return whether the property is set
     *  - a property of a behavior: return whether the property is set
                
2. in /usr/share/nginx/vendor/yiisoft/yii2/BaseYii.php at line 546 – yii\base\Component::__set('contentTable', '{{%matrixcontent_testing}}')
540541542543544545546547548549550551552                         * @param array $properties the property initial values given in terms of name-value pairs.
     * @return object the object itself
     */
    public static function configure($object, $properties)
    {
        foreach ($properties as $name => $value) {
            $object->$name = $value;
        }
 
        return $object;
    }
 
    /**
                
3. in /usr/share/nginx/vendor/yiisoft/yii2/base/BaseObject.php at line 107 – yii\BaseYii::configure(craft\fields\Matrix, ['id' => '55', 'dateCreated' => '2018-10-19 22:49:10', 'dateUpdated' => '2018-10-19 22:49:10', 'groupId' => '1', ...])
101102103104105106107108109110111112113                         *
     * @param array $config name-value pairs that will be used to initialize the object properties
     */
    public function __construct($config = [])
    {
        if (!empty($config)) {
            Yii::configure($this, $config);
        }
        $this->init();
    }
 
    /**
     * Initializes the object.
                
4. in /usr/share/craft-iot-poc/vendor/craftcms/cms/src/helpers/Component.php at line 80 – yii\base\BaseObject::__construct(['id' => '55', 'dateCreated' => '2018-10-19 22:49:10', 'dateUpdated' => '2018-10-19 22:49:10', 'groupId' => '1', ...])
74757677787980818283848586                                throw new MissingComponentException($message);
        }
 
        $config = self::mergeSettings($config);
 
        // Instantiate and return
        return new $class($config);
    }
 
    /**
     * Extracts settings from a given component config, and returns a new config array wiith the settings merged in.
     *
     * @param array $config
                
5. in /usr/share/nginx/vendor/craftcms/cms/src/services/Fields.php at line 506 – craft\helpers\Component::createComponent(['id' => '55', 'dateCreated' => '2018-10-19 22:49:10', 'dateUpdated' => '2018-10-19 22:49:10', 'groupId' => '1', ...], 'craft\base\FieldInterface')
500501502503504505506507508509510511512                            if (is_string($config)) {
            $config = ['type' => $config];
        }
 
        try {
            /** @var Field $field */
            $field = ComponentHelper::createComponent($config, FieldInterface::class);
        } catch (MissingComponentException $e) {
            $config['errorMessage'] = $e->getMessage();
            $config['expectedType'] = $config['type'];
            unset($config['type']);
 
            $field = new MissingField($config);
                
6. in /usr/share/nginx/vendor/craftcms/cms/src/services/Fields.php at line 549 – craft\services\Fields::createField(['id' => '55', 'dateCreated' => '2018-10-19 22:49:10', 'dateUpdated' => '2018-10-19 22:49:10', 'groupId' => '1', ...])
543544545546547548549550551552553554555                                $results = $this->_createFieldQuery()
                ->where(['fields.context' => $missingContexts])
                ->all();
 
            foreach ($results as $result) {
                /** @var Field $field */
                $field = $this->createField($result);
 
                $this->_allFieldsInContext[$field->context][] = $field;
                $this->_fieldsById[$field->id] = $field;
                $this->_fieldsByContextAndHandle[$field->context][$field->handle] = $field;
            }
        }
                
7. in /usr/share/craft-iot-poc/vendor/craftcms/cms/src/elements/db/ElementQuery.php at line 1499 – craft\services\Fields::getAllFields()
1493149414951496149714981499150015011502150315041505                         */
    protected function customFields(): array
    {
        $contentService = Craft::$app->getContent();
        $originalFieldContext = $contentService->fieldContext;
        $contentService->fieldContext = 'global';
        $fields = Craft::$app->getFields()->getAllFields();
        $contentService->fieldContext = $originalFieldContext;
 
        return $fields;
    }
 
    /**
                
8. in /usr/share/craft-iot-poc/vendor/craftcms/cms/src/elements/db/ElementQuery.php at line 1051 – craft\elements\db\ElementQuery::customFields()
1045104610471048104910501051105210531054105510561057                                ->andWhere($this->where)
            ->offset($this->offset)
            ->limit($this->limit)
            ->addParams($this->params);
 
        if ($class::hasContent() && $this->contentTable !== null) {
            $this->customFields = $this->customFields();
            $this->_joinContentTable($class);
        } else {
            $this->customFields = null;
        }
 
        if ($this->distinct) {
                
9. in /usr/share/nginx/vendor/yiisoft/yii2/db/QueryBuilder.php at line 227 – craft\elements\db\ElementQuery::prepare(craft\db\mysql\QueryBuilder)
10. in /usr/share/nginx/vendor/yiisoft/yii2/db/Query.php at line 146 – yii\db\QueryBuilder::build(craft\elements\db\UserQuery)
11. in /usr/share/nginx/vendor/yiisoft/yii2/db/Query.php at line 274 – yii\db\Query::createCommand(craft\db\Connection)
12. in /usr/share/nginx/vendor/craftcms/cms/src/db/Query.php at line 177 – yii\db\Query::one(null)
171172173174175176177178179180181182183                         */
    public function one($db = null)
    {
        $limit = $this->limit;
        $this->limit = 1;
        try {
            $result = parent::one($db);
            // Be more like Yii 2.1
            if ($result === false) {
                $result = null;
            }
        } catch (QueryAbortedException $e) {
            $result = null;
                
13. in /usr/share/craft-iot-poc/vendor/craftcms/cms/src/elements/db/ElementQuery.php at line 1183 – craft\db\Query::one(null)
1177117811791180118111821183118411851186118711881189                        {
        // Cached?
        if (($cachedResult = $this->getCachedResult()) !== null) {
            return reset($cachedResult) ?: null;
        }
 
        if ($row = parent::one($db)) {
            $elements = $this->populate([$row]);
            return reset($elements) ?: null;
        }
 
        return null;
    }
                
14. in /usr/share/craft-iot-poc/vendor/craftcms/cms/src/services/Users.php at line 175 – craft\elements\db\ElementQuery::one()
169170171172173174175176177178179180181                                    'or',
                ['username' => $usernameOrEmail],
                ['email' => $usernameOrEmail]
            ])
            ->addSelect(['users.password', 'users.passwordResetRequired'])
            ->status(null)
            ->one();
    }
 
    /**
     * Returns a user by their UID.
     *
     * ```php
                
15. in /usr/share/craft-iot-poc/src/controllers/ApiController.php at line 497 – craft\services\Users::getUserByUsernameOrEmail('testing')
491492493494495496497498499500501502503                            $password = Craft::$app->getRequest()->getQueryParam('password');
 
        if (!$username || !$password) {
            throw new BadRequestHttpException(self::ERROR_INVALID_CREDENTIALS);
        }
 
        $user = Craft::$app->getUsers()->getUserByUsernameOrEmail($username);
 
        if (!$user) {
            throw new BadRequestHttpException(self::ERROR_INVALID_CREDENTIALS);
        }
 
        if (!$user->authenticate($password)) {
                
16. in /usr/share/craft-iot-poc/src/controllers/ApiController.php at line 115 – nickleguillou\craftiotpoc\controllers\ApiController::getUserFromRequestParams()
109110111112113114115116117118119120121                         * 
     * @throws BadRequestHttpException
     * @return mixed
     */
    public function actionGetApiKeyForUser() {
        try {
            $user = $this->getUserFromRequestParams();
        } catch (\Exception $e) {
            return $this->returnErrorJson($e);
        }
 
        return $this->asJson(['apiKey' => $user->getFieldValue(CraftIotPoc::FIELD_HANDLE_KEY)]);
    }
                
17. nickleguillou\craftiotpoc\controllers\ApiController::actionGetApiKeyForUser()
18. in /usr/share/craft-iot-poc/vendor/yiisoft/yii2/base/InlineAction.php at line 57 – call_user_func_array([nickleguillou\craftiotpoc\controllers\ApiController, 'actionGetApiKeyForUser'], [])
515253545556575859                            $args = $this->controller->bindActionParams($this, $params);
        Yii::debug('Running action: ' . get_class($this->controller) . '::' . $this->actionMethod . '()', __METHOD__);
        if (Yii::$app->requestedParams === null) {
            Yii::$app->requestedParams = $args;
        }
 
        return call_user_func_array([$this->controller, $this->actionMethod], $args);
    }
}
                
19. in /usr/share/craft-iot-poc/vendor/yiisoft/yii2/base/Controller.php at line 157 – yii\base\InlineAction::runWithParams(['p' => '/actions/craft-iot-poc/api/get-a...', 'username' => 'testing', 'password' => 'testing'])
151152153154155156157158159160161162163                            }
 
        $result = null;
 
        if ($runAction && $this->beforeAction($action)) {
            // run the action
            $result = $action->runWithParams($params);
 
            $result = $this->afterAction($action, $result);
 
            // call afterAction on modules
            foreach ($modules as $module) {
                /* @var $module Module */
                
20. in /usr/share/craft-iot-poc/vendor/craftcms/cms/src/web/Controller.php at line 103 – yii\base\Controller::runAction('get-api-key-for-user', ['p' => '/actions/craft-iot-poc/api/get-a...', 'username' => 'testing', 'password' => 'testing'])
979899100101102103104105106107108109                        /**
     * @inheritdoc
     */
    public function runAction($id, $params = [])
    {
        try {
            return parent::runAction($id, $params);
        } catch (\Throwable $e) {
            if (Craft::$app->getRequest()->getAcceptsJson()) {
                Craft::$app->getErrorHandler()->logException($e);
                $message = $e->getMessage();
                if ($e instanceof ClientException) {
                    $statusCode = $e->getCode();
                
21. in /usr/share/nginx/vendor/yiisoft/yii2/base/Module.php at line 528 – craft\web\Controller::runAction('get-api-key-for-user', ['p' => '/actions/craft-iot-poc/api/get-a...', 'username' => 'testing', 'password' => 'testing'])
22. in /usr/share/nginx/vendor/craftcms/cms/src/web/Application.php at line 282 – yii\base\Module::runAction('craft-iot-poc/api/get-api-key-fo...', ['p' => '/actions/craft-iot-poc/api/get-a...', 'username' => 'testing', 'password' => 'testing'])
276277278279280281282283284285286287288                         * @param string $route
     * @param array $params
     * @return Response|null The result of the action, normalized into a Response object
     */
    public function runAction($route, $params = [])
    {
        $result = parent::runAction($route, $params);
 
        if ($result !== null) {
            if ($result instanceof Response) {
                return $result;
            }
 
                
23. in /usr/share/nginx/vendor/craftcms/cms/src/web/Application.php at line 542 – craft\web\Application::runAction('craft-iot-poc/api/get-api-key-fo...', ['p' => '/actions/craft-iot-poc/api/get-a...', 'username' => 'testing', 'password' => 'testing'])
536537538539540541542543544545546547548                                $route = implode('/', $request->getActionSegments());
 
            try {
                Craft::debug("Route requested: '$route'", __METHOD__);
                $this->requestedRoute = $route;
 
                return $this->runAction($route, $_GET);
            } catch (InvalidRouteException $e) {
                throw new NotFoundHttpException(Craft::t('yii', 'Page not found.'), $e->getCode(), $e);
            }
        }
 
        return null;
                
24. in /usr/share/nginx/vendor/craftcms/cms/src/web/Application.php at line 266 – craft\web\Application::_processActionRequest(craft\web\Request)
260261262263264265266267268269270271272                                        throw new ForbiddenHttpException();
                }
            }
        }
 
        // If this is an action request, call the controller
        if (($response = $this->_processActionRequest($request)) !== null) {
            return $response;
        }
 
        // If we're still here, finally let Yii do it's thing.
        return parent::handleRequest($request);
    }
                
25. in /usr/share/nginx/vendor/yiisoft/yii2/base/Application.php at line 386 – craft\web\Application::handleRequest(craft\web\Request)
26. in /usr/share/nginx/web/index.php at line 21 – yii\base\Application::run()
15161718192021                        (new Dotenv\Dotenv(CRAFT_BASE_PATH))->load();
}
 
// Load and run Craft
define('CRAFT_ENVIRONMENT', getenv('ENVIRONMENT') ?: 'production');
$app = require CRAFT_VENDOR_PATH.'/craftcms/cms/bootstrap/web.php';
$app->run();
                
$_GET = [
    'p' => '/actions/craft-iot-poc/api/get-api-key-for-user',
    'username' => 'testing',
    'password' => 'testing',
];

$_COOKIE = [
    '1031b8c41dfff97a311a7ac99863bdc5_identity' => 'f6654e7345ce87d9ba32220bb3423872a4becf62b750d444f2de7c4bbbd7a375a:2:{i:0;s:41:"1031b8c41dfff97a311a7ac99863bdc5_identity";i:1;s:282:"["1","[\\"gBYo7o3qvT9TJcybCw0wkMCbJsPIjwr0rFQUGFGZkNN24fnV4eBb_9A-ZZCRPTOJvTDifvb5c-5NJV_CBdFYov-FI9-lOU5BeN7O\\",\\"dc159435-9f79-42e0-bc9e-d2fe2a311614\\",\\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Safari/605.1.15\\"]",3600]";}',
    '1031b8c41dfff97a311a7ac99863bdc5_username' => '800fb080481e70ec47da2ad75f80f49e555f8f3c7c4b7a9322ffa1ed6e1d1abfa:2:{i:0;s:41:"1031b8c41dfff97a311a7ac99863bdc5_username";i:1;s:5:"admin";}',
    'CRAFT_CSRF_TOKEN' => '57ac3969cdb89ae44aeec8ebf7e663008500761e9e6b42a61693fb3df189d53ba:2:{i:0;s:16:"CRAFT_CSRF_TOKEN";i:1;s:208:"hr0GGFh4suq6ceBQiiJ70n0sPVQyVm0fn7fOpxCO|1da0a100769dcb695d58b45be916156a5101dbc995ffd04708b7d1586610323chr0GGFh4suq6ceBQiiJ70n0sPVQyVm0fn7fOpxCO|1|$2y$13$tdDkk6LZ6PCWFBw0vt1ZlO/tVLSyHE5Za7ZC253pwtyNPNcug8Vuy";}',
    'CraftSessionId' => '4uau906tsh05efqkv9vqhbe4bj',
    'exp_last_activity' => '1539955498',
    'exp_last_visit' => '1539897090',
];

$_SESSION = [
    'bd62416aa8538ede709019a5e113eea5__flash' => [],
    '1031b8c41dfff97a311a7ac99863bdc5__id' => '1',
    '1031b8c41dfff97a311a7ac99863bdc5__expire' => 1539992950,
];
Yii Framework
2018-10-19, 17:49:39

nginx/1.15.4
Yii Framework/2.0.15.1

@brandonkelly
Copy link
Member

craft\fields\Matrix definitely has a $contentTable property, so the error doesn’t make sense. Is this a load-balanced server? Maybe a recent Craft update didn’t get applied successfully for all servers? Or maybe that class is cached by OPcache and not refreshing for some reason?

@nickfreedom
Copy link
Author

Figured it out - my composer.json for my plugin was referencing an older version of Craft.

#facepalm

@brandonkelly
Copy link
Member

Or that :)

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

2 participants