Project

General

Profile

Actions

Bug #65472

closed

TYPO3 6.2.10 Extbase Type Converter can't resolve Tx_Extbase_Persistence_ObjectStorage

Added by Matthias Krappitz almost 9 years ago. Updated almost 9 years ago.

Status:
Closed
Priority:
Must have
Assignee:
-
Category:
Extbase
Target version:
Start date:
2015-03-03
Due date:
% Done:

0%

Estimated time:
TYPO3 Version:
6.2
PHP Version:
5.4
Tags:
Complexity:
Is Regression:
Yes
Sprint Focus:

Description

Upgrading from 6.2.9 to 6.2.10 extbase fails to save values in frontend editing forms (standard f:form and f:input, f:select, ... fields with object / property binding, using standard repository methods add / update) to work on properties defined like this:

/**
 * Some Variable
 *
 * @var Tx_Extbase_Persistence_ObjectStorage<Tx_Someextension_Domain_Model_Somedata>
 * @lazy
 */
protected $someVariable;

results in this on saving:

Uncaught TYPO3 Exception
#1297759968: Exception while property mapping at property path "someVariable":Class Tx_Extbase_Persistence_ObjectStorage<Tx_Someextension_Domain_Model_Somedata> does not exist (More information)

TYPO3\CMS\Extbase\Property\Exception thrown in file
/.../typo3_src-6.2.10/typo3/sysext/extbase/Classes/Property/PropertyMapper.php in line 106.

Similar error like it was fixed in #54289. Probably got re-introduced by the new class loading mechanism in 6.2.10 that seems not to be able to work in this case on not-namespaces classes with the rewritten extbase property manager and the object binding in frontend forms.

Switching core back to 6.2.9 and clearing all caches, my forms save their data with no problem.

If of any importance my CF config looks like this:

'caching' => array(
    'cacheConfigurations' => array(
        'cache_pages' => array(
            'options' => array(
                'compression' => TRUE,
            ),
        ),
        'extbase_object' => array(
            'backend' => '\\TYPO3\\CMS\\Core\\Cache\\Backend\\NullBackend',
        ),
        'extbase_reflection' => array(
            'backend' => '\\TYPO3\\CMS\\Core\\Cache\\Backend\\NullBackend',
        ),
    ),
),

Related issues 1 (0 open1 closed)

Related to TYPO3 Core - Bug #54289: Using Tx_Extbase_Persistence_ObjectStorage doesn't work in rewritten PropertyMapperClosed2013-12-09

Actions
Actions #1

Updated by Christian Kuhn almost 9 years ago

  • Is Regression changed from No to Yes
Actions #2

Updated by Helmut Hummel almost 9 years ago

  • Status changed from New to Needs Feedback

I tried to reproduce with extension news by changing the property like that:

    /**
     * @var Tx_Extbase_Persistence_ObjectStorage<Tx_News_Domain_Model_News>
     * @lazy
     */
    protected $related;

It worked like expected.

Please provide a minimal code example on how to reproduce the error you are seeing.

Actions #3

Updated by Matthias Krappitz almost 9 years ago

Controller Code:

    /**
     * action edit
     *
     * @param Tx_Someextension_Domain_Model_CompanyProfile $companyProfile
     * @return void
     */
    public function editAction($companyProfile) {
        $accessControlService = $this->objectManager->get('Tx_Someextension_Service_AccessControlService');
        if ($accessControlService->hasLoggedInFrontendUser()) {

            // ...

            $this->view->assign('companyProfile', $companyProfile);
            $this->view->assign('sectorOptions', $this->sectorRepository->findAll());

        }
    }

    // ensuring the minimum fields field in current foreign language
    public function initializeUpdateAction () {
        $ensureOverlayFieldsFilled = array('title', 'address', 'city');
        $languages = array(1, 2);
        foreach ($languages as $language) {
            foreach ($ensureOverlayFieldsFilled as $field) {
                if (trim($_REQUEST['tx_someextension_someextension']['overlay'][$language][$field]) == '') {
                    // ...
                } 
            }
        }
    }

    /**
     * action update
     *
     * @param Tx_Someextension_Domain_Model_CompanyProfile $companyProfile
     * @return void
     */
    public function updateAction(Tx_Someextension_Domain_Model_CompanyProfile $companyProfile) {
        $accessControlService = $this->objectManager->get('Tx_Someextension_Service_AccessControlService');
        if ($accessControlService->hasLoggedInFrontendUser()) {

            ...
            $this->companyProfileRepository->update($companyProfile);
            ...            

            $this->flashMessageContainer->add(
                Tx_Extbase_Utility_Localization::translate('your_data_has_been_saved', $this->extensionName), 
                NULL, 
                t3lib_Flashmessage::OK
            );

            $this->redirect('edit', NULL, NULL, array('companyProfile' => $companyProfile->getUid()));
        }
    }

Model


class Tx_Someextension_Domain_Model_CompanyProfile extends Tx_Extbase_DomainObject_AbstractEntity {

    // ...

    /**
     * Sectors
     *
     * @var Tx_Extbase_Persistence_ObjectStorage<Tx_Someextension_Domain_Model_Sector>
     * @lazy
     */
    protected $sectors;

    // ...

    /**
     * __construct
     *
     * @return void
     */
    public function __construct() {
        //Do not remove the next line: It would break the functionality
        $this->initStorageObjects();
    }

    /**
     * Initializes all Tx_Extbase_Persistence_ObjectStorage properties.
     *
     * @return void
     */
    protected function initStorageObjects() {
        /**
         * Do not modify this method!
         * It will be rewritten on each save in the extension builder
         * You may modify the constructor of this class instead
         */
        $this->sectors = new Tx_Extbase_Persistence_ObjectStorage();
        // ...
    }

    // ...

    /**
     * Adds a Sector
     *
     * @param Tx_Someextension_Domain_Model_Sector $sector
     * @return void
     */
    public function addSector(Tx_Someextension_Domain_Model_Sector $sector) {
        $this->sectors->attach($sector);
    }

    /**
     * Removes a Sector
     *
     * @param Tx_Someextension_Domain_Model_Sector $sectorToRemove The Sector to be removed
     * @return void
     */
    public function removeSector(Tx_Someextension_Domain_Model_Sector $sectorToRemove) {
        $this->sectors->detach($sectorToRemove);
    }

    /**
     * Returns the sectors
     *
     * @return Tx_Extbase_Persistence_ObjectStorage<Tx_Someextension_Domain_Model_Sector> $sectors
     */
    public function getSectors() {
        return $this->sectors;
    }

    /**
     * Sets the sectors
     *
     * @param Tx_Extbase_Persistence_ObjectStorage<Tx_Someextension_Domain_Model_Sector> $sectors
     * @return void
     */
    public function setSectors(Tx_Extbase_Persistence_ObjectStorage $sectors) {
        $this->sectors = $sectors;
    }

}

Repository

class Tx_Somextension_Domain_Repository_CompanyProfileRepository extends Tx_Extbase_Persistence_Repository {

    /**
     * defaultOrderings
     *
     * @var array
     */
    protected $defaultOrderings = array(
        'profiletype' => Tx_Extbase_Persistence_QueryInterface::ORDER_DESCENDING,
        'crdate' => Tx_Extbase_Persistence_QueryInterface::ORDER_DESCENDING,
     );

    /**
     * initializeObject
     *
     * @info necessary because of fundamental language overlay handling change in 6.2
     * @return void
     */
     public function initializeObject() {
        $defaultQuerySettings = $this->createQuery()->getQuerySettings();
        $defaultQuerySettings->setRespectSysLanguage(FALSE);
        if ($GLOBALS['TSFE']->sys_language_uid) {
            $defaultQuerySettings->setSysLanguageUid($GLOBALS['TSFE']->sys_language_uid);
        }
        $this->setDefaultQuerySettings($defaultQuerySettings);
    }

    // ...

}

Edit Template

        <f:flashMessages renderMode="div" />

        <f:render partial="FormErrors" arguments="{_all}"/>

        <f:form action="update" name="companyProfile" enctype="multipart/form-data" object="{companyProfile}">
            ...
            <f:form.select property="sectors" options="{sectorOptions}" optionValueField="uid" optionLabelField="title" multiple="true" size="8" />
            ...
            <f:render partial="Form/RequiredNotice" />
            <f:render partial="Form/Submit" arguments="{buttonText: 'save'}" />
        </f:form>

Error stacktrace


Uncaught TYPO3 Exception
#1297759968: Exception while property mapping at property path "sectors":Class Tx_Extbase_Persistence_ObjectStorage<Tx_Someextension_Domain_Model_Sector> does not exist (More information)

TYPO3\CMS\Extbase\Property\Exception thrown in file
/.../www/typo3_src-6.2.10/typo3/sysext/extbase/Classes/Property/PropertyMapper.php in line 106.

16 TYPO3\CMS\Extbase\Property\PropertyMapper::convert(array, "Tx_Someextension_Domain_Model_CompanyProfile", TYPO3\CMS\Extbase\Mvc\Controller\MvcPropertyMappingConfiguration)

/.../www/typo3_src-6.2.10/typo3/sysext/extbase/Classes/Mvc/Controller/Argument.php:

00372:     return $this;
00373:    }

00374:    $this->value = $this->propertyMapper->convert($rawValue, $this->dataType, $this->propertyMappingConfiguration);

00375:    $this->validationResults = $this->propertyMapper->getMessages();
00376:    if ($this->validator !== NULL) {

15 TYPO3\CMS\Extbase\Mvc\Controller\Argument::setValue(array)

/.../www/typo3_src-6.2.10/typo3/sysext/extbase/Classes/Mvc/Controller/AbstractController.php:

00421:     $argumentName = $argument->getName();
00422:     if ($this->request->hasArgument($argumentName)) {

00423:      $argument->setValue($this->request->getArgument($argumentName));

00424:     } elseif ($argument->isRequired()) {
00425:      throw new \TYPO3\CMS\Extbase\Mvc\Controller\Exception\RequiredArgumentMissingException('Required argument "' . $argumentName . '" is not set.', 1298012500);

14 TYPO3\CMS\Extbase\Mvc\Controller\AbstractController::mapRequestArgumentsToControllerArguments()

/.../www/typo3_src-6.2.10/typo3/sysext/extbase/Classes/Mvc/Controller/ActionController.php:

00148:    call_user_func(array($this, $actionInitializationMethodName));
00149:   }

00150:   $this->mapRequestArgumentsToControllerArguments();

00151:   $this->checkRequestHash();
00152:   $this->controllerContext = $this->buildControllerContext();

13 TYPO3\CMS\Extbase\Mvc\Controller\ActionController::processRequest(TYPO3\CMS\Extbase\Mvc\Web\Request, TYPO3\CMS\Extbase\Mvc\Web\Response)

/.../www/typo3_src-6.2.10/typo3/sysext/extbase/Classes/Mvc/Dispatcher.php:

00067:    $controller = $this->resolveController($request);
00068:    try {

00069:     $controller->processRequest($request, $response);

00070:    } catch (\TYPO3\CMS\Extbase\Mvc\Exception\StopActionException $ignoredException) {
00071:    }

12 TYPO3\CMS\Extbase\Mvc\Dispatcher::dispatch(TYPO3\CMS\Extbase\Mvc\Web\Request, TYPO3\CMS\Extbase\Mvc\Web\Response)

/.../www/typo3_src-6.2.10/typo3/sysext/extbase/Classes/Mvc/Web/FrontendRequestHandler.php:

00054:   /** @var $response \TYPO3\CMS\Extbase\Mvc\ResponseInterface */
00055:   $response = $this->objectManager->get('TYPO3\\CMS\\Extbase\\Mvc\\Web\\Response');

00056:   $this->dispatcher->dispatch($request, $response);

00057:   return $response;
00058:  }

11 TYPO3\CMS\Extbase\Mvc\Web\FrontendRequestHandler::handleRequest()

/.../www/typo3_src-6.2.10/typo3/sysext/extbase/Classes/Core/Bootstrap.php:

00193:   $requestHandler = $requestHandlerResolver->resolveRequestHandler();
00194: 

00195:   $response = $requestHandler->handleRequest();

00196:   // If response is NULL after handling the request we need to stop
00197:   // This happens for instance, when a USER object was converted to a USER_INT

10 TYPO3\CMS\Extbase\Core\Bootstrap::handleRequest()

/.../www/typo3_src-6.2.10/typo3/sysext/extbase/Classes/Core/Bootstrap.php:

00182:  public function run($content, $configuration) {
00183:   $this->initialize($configuration);

00184:   return $this->handleRequest();

00185:  }
00186: 

9 TYPO3\CMS\Extbase\Core\Bootstrap::run("", array)

8 call_user_func_array(array, array)

/is/htdocs/wp1148156_S2ZW3VJZ68/www/typo3_src-6.2.10/typo3/sysext/frontend/Classes/ContentObject/ContentObjectRenderer.php:

06620:       $content,
06621:       $conf

06622:      ));

06623:     } else {
06624:      $GLOBALS['TT']->setTSlogMessage('Method "' . $parts[1] . '" did not exist in class "' . $parts[0] . '"', 3);

...

Actions #4

Updated by Matthias Krappitz almost 9 years ago

Maybe this also of importance: I have registered an additional typeconverter to allow NULL values on object properties:

ext_localconf.php

Tx_Extbase_Utility_Extension::registerTypeConverter('Tx_Someextension_Property_TypeConverter_PersistentObjectConverter');

Type Converter Class


class Tx_Someextension_Property_TypeConverter_PersistentObjectConverter extends Tx_Extbase_Property_TypeConverter_PersistentObjectConverter {

    /**
     * Set this to a higher value then the core has,
     * 
     * @var integer
     */
    protected $priority = 2;

    /**
     * Call core functionality.
     * If an exception rises up, check whether it's because the parameter was not a valid uid.
     * Then check whether it's null and return null or let the exception pass.
     *
     * @param mixed $identity
     * @param string $targetType
     * @return object
     * @author Daniel Siepmann <daniel.siepmann@wfp2.com>
     */
    protected function fetchObjectFromPersistence($identity, $targetType) {
        try {
            return parent::fetchObjectFromPersistence($identity, $targetType);
        } catch (Exception $e) {
            if ($e->getCode() === 1297931020 && is_string($identity) && (strtolower($identity) === 'null' || $identity === '' || $identity === NULL)) {
                return NULL;
            }
            throw $e;
        }
    }

}

But error remains even with this Typeconverter disabled.

Actions #5

Updated by Matthias Krappitz almost 9 years ago

You can close this one. This error is gone with 6.2.11. So it was a regression of the new class loading mechanism.

Actions #6

Updated by Riccardo De Contardi almost 9 years ago

  • Status changed from Needs Feedback to Closed

close as per request of the reporter.

Actions

Also available in: Atom PDF