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

Field Caching Causes Exception with Multiple Changes in the Same Request #2985

Closed
sbossarte opened this issue Jun 12, 2018 · 0 comments
Closed

Comments

@sbossarte
Copy link
Contributor

sbossarte commented Jun 12, 2018

Description

Performing certain operations after deleting a field can cause an exception.

For example, upon deleting a category group, Craft attempts to retrieve all categories for the group in order to delete them, too. When it selects these categories, it also attempts to select columns for fields deleted during the same request. It would seem this is caused by deleted fields not being removed from the internal caches within \craft\services\Fields.

The same issue is caused by deleting a tag group after a field, and possibly other combinations (such as anything that uses an ElementQuery).

Stack Trace:

yii\db\Exception: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'content.field_testTextField' in 'field list'
The SQL being executed was: SELECT `elements`.`id`, `elements`.`fieldLayoutId`, `elements`.`uid`, `elements`.`enabled`, `elements`.`archived`, `elements`.`dateCreated`, `elements`.`dateUpdated`, `elements_sites`.`slug`, `elements_sites`.`uri`, `elements_sites`.`enabled` AS `enabledForSite`, `categories`.`groupId`, `content`.`id` AS `contentId`, `content`.`title`, `content`.`field_testTextField`, `structureelements`.`root`, `structureelements`.`lft`, `structureelements`.`rgt`, `structureelements`.`level`
FROM (SELECT `elements`.`id` AS `elementsId`, `elements_sites`.`id` AS `elementsSitesId`, `content`.`id` AS `contentId`
FROM `elements` `elements`
INNER JOIN `categories` `categories` ON `categories`.`id` = `elements`.`id`
INNER JOIN `elements_sites` `elements_sites` ON `elements_sites`.`elementId` = `elements`.`id`
INNER JOIN `content` `content` ON `content`.`elementId` = `elements`.`id`
INNER JOIN `structureelements` `structureelements` ON `structureelements`.`elementId` = `elements`.`id`
WHERE (`categories`.`groupId`=16) AND (`elements_sites`.`siteId`='1') AND (`content`.`siteId`='1') AND (`elements`.`archived`=FALSE) AND (`structureelements`.`structureId`=16)
ORDER BY `structureelements`.`lft`, `elements`.`dateCreated` DESC) `subquery`
INNER JOIN `categories` `categories` ON `categories`.`id` = `subquery`.`elementsId`
INNER JOIN `elements` `elements` ON `elements`.`id` = `subquery`.`elementsId`
INNER JOIN `elements_sites` `elements_sites` ON `elements_sites`.`id` = `subquery`.`elementsSitesId`
INNER JOIN `content` `content` ON `content`.`id` = `subquery`.`contentId`
INNER JOIN `structureelements` `structureelements` ON `structureelements`.`elementId` = `subquery`.`elementsId`
ORDER BY `structureelements`.`lft`, `elements`.`dateCreated` DESC in /var/www/vendor/yiisoft/yii2/db/Schema.php:664
Stack trace:
#0 /var/www/vendor/yiisoft/yii2/db/Command.php(1263): yii\db\Schema->convertException(Object(PDOException), 'SELECT `element...')
#1 /var/www/vendor/yiisoft/yii2/db/Command.php(1148): yii\db\Command->internalExecute('SELECT `element...')
#2 /var/www/vendor/yiisoft/yii2/db/Command.php(399): yii\db\Command->queryInternal('fetchAll', NULL)
#3 /var/www/vendor/yiisoft/yii2/db/Query.php(237): yii\db\Command->queryAll()
#4 /var/www/vendor/craftcms/cms/src/db/Query.php(155): yii\db\Query->all(NULL)
#5 /var/www/vendor/craftcms/cms/src/elements/db/ElementQuery.php(1069): craft\db\Query->all(NULL)
#6 /var/www/vendor/craftcms/cms/src/services/Categories.php(535): craft\elements\db\ElementQuery->all()
#7 /var/www/vendor/namespace/package/src/controllers/TestController.php(266): craft\services\Categories->deleteGroupById(16)
#8 [internal function]: namespace\package\controllers\TestController->actionRemoveTestData()
#9 /var/www/vendor/yiisoft/yii2/base/InlineAction.php(57): call_user_func_array(Array, Array)
#10 /var/www/vendor/yiisoft/yii2/base/Controller.php(157): yii\base\InlineAction->runWithParams(Array)
#11 /var/www/vendor/craftcms/cms/src/web/Controller.php(103): yii\base\Controller->runAction('remove-test-dat...', Array)
#12 /var/www/vendor/yiisoft/yii2/base/Module.php(528): craft\web\Controller->runAction('remove-test-dat...', Array)
#13 /var/www/vendor/craftcms/cms/src/web/Application.php(273): yii\base\Module->runAction('package/test/...', Array)
#14 /var/www/vendor/craftcms/cms/src/web/Application.php(521): craft\web\Application->runAction('package/test/...', Array)
#15 /var/www/vendor/craftcms/cms/src/web/Application.php(257): craft\web\Application->_processActionRequest(Object(craft\web\Request))
#16 /var/www/vendor/yiisoft/yii2/base/Application.php(386): craft\web\Application->handleRequest(Object(craft\web\Request))
#17 /var/www/html/index.php(21): yii\base\Application->run()
#18 {main}

Steps to reproduce

For the example above, create a simple category group and text field:

// Create category group.
$categorygroup = new \craft\models\CategoryGroup();
$categorygroup->name = 'Test Category Group';
$categorygroup->handle = 'testCategoryGroup';
$categorygroup_sitesettings = [];
foreach (\Craft::$app->getSites()->getAllSites() as $site) {
	$sitesettings = new \craft\models\CategoryGroup_SiteSettings();
	$sitesettings->siteId = $site->id;
	$sitesettings->hasUrls = false;
	$categorygroup_sitesettings[$site->id] = $sitesettings;
}
$categorygroup->setSiteSettings($categorygroup_sitesettings);
$fieldlayout = \Craft::$app->getFields()->assembleLayout([]);
$fieldlayout->type = \craft\elements\Category::class;
$categorygroup->setFieldLayout($fieldlayout);
if (!\Craft::$app->getCategories()->saveGroup($categorygroup)) {
	return $this->asJson('Something broke');
}

// Create text field.
$textfield = \Craft::$app->getFields()->createField([
	'type' => \craft\fields\PlainText::class,
	'groupId' => \Craft::$app->getFields()->getAllGroups()[0]->id,
	'name' => 'Test Text Field',
	'handle' => 'testTextField',
	'instructions' => 'Just a test.',
	'translationMethod' => \craft\base\Field::TRANSLATION_METHOD_NONE,
	'translationKeyFormat' => null,
	'settings' => []
]);
if (!\Craft::$app->getFields()->saveField($textfield)) {
	return $this->asJson('Something broke');
}

Then, in another request, delete the field, and then the category group:

// Delete text field.
$field = \Craft::$app->getFields()->getFieldByHandle('testTextField');
if ($field != null) {
	\Craft::$app->getFields()->deleteField($field);
}

// Delete category group.
$categorygroup = \Craft::$app->getCategories()->getGroupByHandle('testCategoryGroup');
if ($categorygroup != null) {
	\Craft::$app->getCategories()->deleteGroupById($categorygroup->id); // Fails here on first run.
}

Deleting the category group fails on first run because the deleted field is still selected, but on a second run it will work as intended, as the field won't have existed when the request started.

Additional info

  • Craft version: 3.0.10.3
  • PHP version: 7.1.18
  • Database driver & version: MySQL 5.5.56
  • Plugins & versions: None
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

1 participant