I'm struggling with fixing this class exception error. If anyone could help it would be greatly appreciated, Thanks!
BACKGROUND INFO:
After installing a Dashnex plugin on my wordpress site & then uninstalling WP Quick cache I am getting this error message. Please note that quick cache is fully uninstalled & the wp-config.php file includes no quick cache instructions.
ERROR MESSAGE:
Fatal error: Uncaught exception 'ReflectionException' with message 'Class \quick_cache does not exist' in /home/cal108/public_html/wp-content/plugins/dashnex-plugin/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/AnnotationDriver.php:246 Stack trace: #0 /home/cal108/public_html/wp-content/plugins/dashnex-plugin/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/AnnotationDriver.php(246): ReflectionClass->__construct('\quick_cache') #1 /home/cal108/public_html/wp-content/plugins/dashnex-plugin/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php(113): Doctrine\Common\Persistence\Mapping\Driver\AnnotationDriver->getAllClassNames() #2 /home/cal108/public_html/wp-content/plugins/dashnex-plugin/DashNex/Doctrine/MagicSchema.php(18): Doctrine\Common\Persistence\Mapping\AbstractClassMetadataFactory->getAllMetadata() #3 /home/cal108/public_html/wp-content/plugins/dashnex-plugin/DashNex/Doctrine/MagicSchema.php(37): DashNex\Doctrine\MagicSchema->Get in /home/cal108/public_html/wp-content/plugins/dashnex-plugin/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/AnnotationDriver.php on line 246
AnnotationDriver.php Line 246
$rc = new \ReflectionClass($className);
AnnotationDriver.php File
<?php
abstract class AnnotationDriver implements MappingDriver
{
/**
* The AnnotationReader.
*
* #var AnnotationReader
*/
protected $reader;
/**
* The paths where to look for mapping files.
*
* #var array
*/
protected $paths = array();
/**
* The paths excluded from path where to look for mapping files.
*
* #var array
*/
protected $excludePaths = array();
/**
* The file extension of mapping documents.
*
* #var string
*/
protected $fileExtension = '.php';
/**
* Cache for AnnotationDriver#getAllClassNames().
*
* #var array|null
*/
protected $classNames;
/**
* Name of the entity annotations as keys.
*
* #var array
*/
protected $entityAnnotationClasses = array();
/**
* Initializes a new AnnotationDriver that uses the given AnnotationReader for reading
* docblock annotations.
*
* #param AnnotationReader $reader The AnnotationReader to use, duck-typed.
* #param string|array|null $paths One or multiple paths where mapping classes can be found.
*/
public function __construct($reader, $paths = null)
{
$this->reader = $reader;
if ($paths) {
$this->addPaths((array) $paths);
}
}
/**
* Appends lookup paths to metadata driver.
*
* #param array $paths
*
* #return void
*/
public function addPaths(array $paths)
{
$this->paths = array_unique(array_merge($this->paths, $paths));
}
/**
* Retrieves the defined metadata lookup paths.
*
* #return array
*/
public function getPaths()
{
return $this->paths;
}
/**
* Append exclude lookup paths to metadata driver.
*
* #param array $paths
*/
public function addExcludePaths(array $paths)
{
$this->excludePaths = array_unique(array_merge($this->excludePaths, $paths));
}
/**
* Retrieve the defined metadata lookup exclude paths.
*
* #return array
*/
public function getExcludePaths()
{
return $this->excludePaths;
}
/**
* Retrieve the current annotation reader
*
* #return AnnotationReader
*/
public function getReader()
{
return $this->reader;
}
/**
* Gets the file extension used to look for mapping files under.
*
* #return string
*/
public function getFileExtension()
{
return $this->fileExtension;
}
/**
* Sets the file extension used to look for mapping files under.
*
* #param string $fileExtension The file extension to set.
*
* #return void
*/
public function setFileExtension($fileExtension)
{
$this->fileExtension = $fileExtension;
}
/**
* Returns whether the class with the specified name is transient. Only non-transient
* classes, that is entities and mapped superclasses, should have their metadata loaded.
*
* A class is non-transient if it is annotated with an annotation
* from the {#see AnnotationDriver::entityAnnotationClasses}.
*
* #param string $className
*
* #return boolean
*/
public function isTransient($className)
{
$classAnnotations = $this->reader->getClassAnnotations(new \ReflectionClass($className));
foreach ($classAnnotations as $annot) {
if (isset($this->entityAnnotationClasses[get_class($annot)])) {
return false;
}
}
return true;
}
/**
* {#inheritDoc}
*/
public function getAllClassNames()
{
if ($this->classNames !== null) {
return $this->classNames;
}
if (!$this->paths) {
throw MappingException::pathRequired();
}
$classes = array();
$includedFiles = array();
foreach ($this->paths as $path) {
if ( ! is_dir($path)) {
throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path);
}
$iterator = new \RegexIterator(
new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS),
\RecursiveIteratorIterator::LEAVES_ONLY
),
'/^.+' . preg_quote($this->fileExtension) . '$/i',
\RecursiveRegexIterator::GET_MATCH
);
foreach ($iterator as $file) {
$sourceFile = $file[0];
if ( ! preg_match('(^phar:)i', $sourceFile)) {
$sourceFile = realpath($sourceFile);
}
foreach ($this->excludePaths as $excludePath) {
$exclude = str_replace('\\', '/', realpath($excludePath));
$current = str_replace('\\', '/', $sourceFile);
if (strpos($current, $exclude) !== false) {
continue 2;
}
}
require_once $sourceFile;
$includedFiles[] = $sourceFile;
}
}
$declared = get_declared_classes();
foreach ($declared as $className) {
$rc = new \ReflectionClass($className);
$sourceFile = $rc->getFileName();
if (in_array($sourceFile, $includedFiles) && ! $this->isTransient($className)) {
$classes[] = $className;
}
}
$this->classNames = $classes;
return $classes;
}
}
I WAS ABLE TO SOLVE MY PROBLEM. HELP NOT NEEDED ANYMORE :)
FOR ANYONE THIS MAY STILL HELP:
I DISABLED ALL OF MY PLUGINS BY RENAMING THEM IT IN CPANEL, THEN BROUGHT EACH ONE ONLINE AGAIN TO KNOW IF THE DASHNEX PLUGIN WAS THE CULPRIT. IT WAS. THEN RENAMED ALL PLUGINS AGAIN & ALL WORKING FINE.
I DON'T THINK THE DASHNEX PLUGIN LIKED MY UNINSTALLING WP QUICK CACHE. BUT REINSTALLING IT AFTER WP QUCIK CACHE HAD ALREADY BEEN UNINSTALLED WORKED FINE. THIS IS THE 2ND TIME I'VE HAD PROBLEMS WHEN QUICK CACHE HAS BEEN UNINSTALLED SO BE WARY OF IT. MAYBE BEST TO JUST DEACTIVATE IT & ONLY UNINSTALL IT ON A BRAND NEW SITE BEFORE INSTALLING ANY OTHER PLUGINS.
DIDN'T END UP NEEDING TO TWEAK THE PHP CODE.
Related
In working on a new cakephp application I get the error message Cannot use 'Object' as class name as it is reserved in C:\projectfolder\ckphp-demo\cake\libs\object.php on line 33.
The project is a legacy web application using cakePHP 1.3.17 with php version PHP 7.4.28 on developement server. see below code for object.php
line 33 is where class Object { starts
<?php
/**
* Object class, allowing __construct and __destruct in PHP4.
*
* Also includes methods for logging and the special method RequestAction,
* to call other Controllers' Actions from anywhere.
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* #copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
* #link http://cakephp.org CakePHP(tm) Project
* #package cake
* #subpackage cake.cake.libs
* #since CakePHP(tm) v 0.2.9
* #license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Object class, allowing __construct and __destruct in PHP4.
*
* Also includes methods for logging and the special method RequestAction,
* to call other Controllers' Actions from anywhere.
*
* #package cake
* #subpackage cake.cake.libs
*/
class Object {
/**
* A hack to support __construct() on PHP 4
* Hint: descendant classes have no PHP4 class_name() constructors,
* so this constructor gets called first and calls the top-layer __construct()
* which (if present) should call parent::__construct()
*
* #return Object
*/
function Object() {
$args = func_get_args();
if (method_exists($this, '__destruct')) {
register_shutdown_function (array(&$this, '__destruct'));
}
call_user_func_array(array(&$this, '__construct'), $args);
}
/**
* Class constructor, overridden in descendant classes.
*/
function __construct() {
}
/**
* Object-to-string conversion.
* Each class can override this method as necessary.
*
* #return string The name of this class
* #access public
*/
function toString() {
$class = get_class($this);
return $class;
}
/**
* Calls a controller's method from any location. Can be used to connect controllers together
* or tie plugins into a main application. requestAction can be used to return rendered views
* or fetch the return value from controller actions.
*
* #param mixed $url String or array-based url.
* #param array $extra if array includes the key "return" it sets the AutoRender to true.
* #return mixed Boolean true or false on success/failure, or contents
* of rendered action if 'return' is set in $extra.
* #access public
*/
function requestAction($url, $extra = array()) {
if (empty($url)) {
return false;
}
if (!class_exists('dispatcher')) {
require CAKE . 'dispatcher.php';
}
if (in_array('return', $extra, true)) {
$extra = array_merge($extra, array('return' => 0, 'autoRender' => 1));
}
if (is_array($url) && !isset($extra['url'])) {
$extra['url'] = array();
}
$params = array_merge(array('autoRender' => 0, 'return' => 1, 'bare' => 1, 'requested' => 1), $extra);
$dispatcher = new Dispatcher;
return $dispatcher->dispatch($url, $params);
}
/**
* Calls a method on this object with the given parameters. Provides an OO wrapper
* for `call_user_func_array`
*
* #param string $method Name of the method to call
* #param array $params Parameter list to use when calling $method
* #return mixed Returns the result of the method call
* #access public
*/
function dispatchMethod($method, $params = array()) {
switch (count($params)) {
case 0:
return $this->{$method}();
case 1:
return $this->{$method}($params[0]);
case 2:
return $this->{$method}($params[0], $params[1]);
case 3:
return $this->{$method}($params[0], $params[1], $params[2]);
case 4:
return $this->{$method}($params[0], $params[1], $params[2], $params[3]);
case 5:
return $this->{$method}($params[0], $params[1], $params[2], $params[3], $params[4]);
default:
return call_user_func_array(array(&$this, $method), $params);
break;
}
}
/**
* Stop execution of the current script. Wraps exit() making
* testing easier.
*
* #param $status see http://php.net/exit for values
* #return void
* #access public
*/
function _stop($status = 0) {
exit($status);
}
/**
* Convience method to write a message to CakeLog. See CakeLog::write()
* for more information on writing to logs.
*
* #param string $msg Log message
* #param integer $type Error type constant. Defined in app/config/core.php.
* #return boolean Success of log write
* #access public
*/
function log($msg, $type = LOG_ERROR) {
if (!class_exists('CakeLog')) {
require LIBS . 'cake_log.php';
}
if (!is_string($msg)) {
$msg = print_r($msg, true);
}
return CakeLog::write($type, $msg);
}
/**
* Allows setting of multiple properties of the object in a single line of code. Will only set
* properties that are part of a class declaration.
*
* #param array $properties An associative array containing properties and corresponding values.
* #return void
* #access protected
*/
function _set($properties = array()) {
if (is_array($properties) && !empty($properties)) {
$vars = get_object_vars($this);
foreach ($properties as $key => $val) {
if (array_key_exists($key, $vars)) {
$this->{$key} = $val;
}
}
}
}
/**
* Used to report user friendly errors.
* If there is a file app/error.php or app/app_error.php this file will be loaded
* error.php is the AppError class it should extend ErrorHandler class.
*
* #param string $method Method to be called in the error class (AppError or ErrorHandler classes)
* #param array $messages Message that is to be displayed by the error class
* #return error message
* #access public
*/
function cakeError($method, $messages = array()) {
if (!class_exists('ErrorHandler')) {
App::import('Core', 'Error');
if (file_exists(APP . 'error.php')) {
include_once (APP . 'error.php');
} elseif (file_exists(APP . 'app_error.php')) {
include_once (APP . 'app_error.php');
}
}
if (class_exists('AppError')) {
$error = new AppError($method, $messages);
} else {
$error = new ErrorHandler($method, $messages);
}
return $error;
}
/**
* Checks for a persistent class file, if found file is opened and true returned
* If file is not found a file is created and false returned
* If used in other locations of the model you should choose a unique name for the persistent file
* There are many uses for this method, see manual for examples
*
* #param string $name name of the class to persist
* #param string $object the object to persist
* #return boolean Success
* #access protected
* #todo add examples to manual
*/
function _persist($name, $return = null, &$object, $type = null) {
$file = CACHE . 'persistent' . DS . strtolower($name) . '.php';
if ($return === null) {
if (!file_exists($file)) {
return false;
} else {
return true;
}
}
if (!file_exists($file)) {
$this->_savePersistent($name, $object);
return false;
} else {
$this->__openPersistent($name, $type);
return true;
}
}
/**
* You should choose a unique name for the persistent file
*
* There are many uses for this method, see manual for examples
*
* #param string $name name used for object to cache
* #param object $object the object to persist
* #return boolean true on save, throws error if file can not be created
* #access protected
*/
function _savePersistent($name, &$object) {
$file = 'persistent' . DS . strtolower($name) . '.php';
$objectArray = array(&$object);
$data = str_replace('\\', '\\\\', serialize($objectArray));
$data = '<?php $' . $name . ' = \'' . str_replace('\'', '\\\'', $data) . '\' ?>';
$duration = '+999 days';
if (Configure::read() >= 1) {
$duration = '+10 seconds';
}
cache($file, $data, $duration);
}
/**
* Open the persistent class file for reading
* Used by Object::_persist()
*
* #param string $name Name of persisted class
* #param string $type Type of persistance (e.g: registry)
* #return void
* #access private
*/
function __openPersistent($name, $type = null) {
$file = CACHE . 'persistent' . DS . strtolower($name) . '.php';
include($file);
switch ($type) {
case 'registry':
$vars = unserialize(${$name});
foreach ($vars['0'] as $key => $value) {
if (strpos($key, '_behavior') !== false) {
App::import('Behavior', Inflector::classify(substr($key, 0, -9)));
} else {
App::import('Model', Inflector::camelize($key));
}
unset ($value);
}
unset($vars);
$vars = unserialize(${$name});
foreach ($vars['0'] as $key => $value) {
ClassRegistry::addObject($key, $value);
unset ($value);
}
unset($vars);
break;
default:
$vars = unserialize(${$name});
$this->{$name} = $vars['0'];
unset($vars);
break;
}
}
}
I'm trying to save a collection of files.
When I save a collection, it goes into the DB without a hitch. Same with files.
But when I add a collection and a new file in the same request (like here in the upload function).
Whenever I then ask doctrine to get me the files in the collection (in this case the one new file).
It always responds with an empty ArrayCollection.
If I do a seperate get HTTP request and ask for the collection afterwards, it shows me the correct arrayCollection containing my one new file.
I have tried every way of persisting and flushing the entities, as well as changing cascade annotations, so far nothing has worked.
I have tried clearing the doctrine cache as well.
The Annotations seem to be correct because calling a getCollection()->getFiles() results in an ArrayCollection containing the files linked to the collection. It just doesn't seem to work right after creating both entities and linking them together.
Thank you very much for your help, code is down below.
This is my collection. Which contains files as an ArrayCollection.
/**
* #Entity #Table(name="LH_FileCollections")
**/
class LhFileCollection extends RootModel
{
/**
* #Column(type="string")
*/
protected $title;
/**
* #OneToMany(targetEntity="LhFile", mappedBy="collection")
*/
protected $files;
//Getters and Setters
}
This is my File class.
/**
* #Entity #Table(name="LH_Files")
**/
class LhFile extends RootModel
{
/**
* #Column(type="string")
*/
protected $name;
/**
* #Column(type="string")
*/
protected $type;
/**
* #Column(name="file_hash", type="string")
*/
protected $fileHash;
/**
* #ManyToOne(targetEntity="LhFileCollection", inversedBy="files", cascade={"persist"})
* #JoinColumn(name="collection_id", referencedColumnName="id")
*/
protected $collection;
//Getters and Setters
}
This is my save filecollection function.
/**
* #return array|string
* #throws \Doctrine\ORM\ORMException
* #throws \Doctrine\ORM\OptimisticLockException
* #throws \Exception
*/
public function fileUpload(
$title,
$attachment = null,
$allowedFileTypes = null,
$maxAllowedFileSize = 5000000
) {
//Create collection
$collection = $this->fileCollectionRepository->add($title);
foreach ($_FILES as $file) {
if ($allowedFileTypes !== null) {
$errors = $this->fileCheck($file, $allowedFileTypes, $maxAllowedFileSize);
if (!empty($errors)) {
return $errors;
}
}
$this->saveFile($file, $collection);
}
return $collection;
}
This is my save file function.
/**
* #param $file
* #param $collection
* #return LhFile
* #throws \Doctrine\ORM\ORMException
* #throws \Doctrine\ORM\OptimisticLockException
*/
private function saveFile($file, $collection)
{
$currentDir = getcwd();
$uploadDir = $currentDir . '/Data/Uploads/';
$extension = pathinfo($file['name'], PATHINFO_EXTENSION);
$uniqueName = uniqid() . "_" . time() . '.' . $extension;
move_uploaded_file($file['tmp_name'], $uploadDir . $uniqueName);
$metaData = "...";
$file = $this->fileRepository->add(
$file['name'],
$file['size'],
$extension,
$metaData,
$uniqueName,
$collection
);
return $file;
}
And lastly the repository functions:
Filecollection:
/**
* #param $fileCollection
* #throws \Doctrine\ORM\ORMException
* #throws \Doctrine\ORM\OptimisticLockException
*/
public function add($title)
{
$fileCollection = new LhFileCollection();
$fileCollection->setTitle($title);
$this->em->persist($fileCollection);
$this->em->flush();
return $fileCollection;
}
And File
/**
* #param $file
* #throws \Doctrine\ORM\ORMException
* #throws \Doctrine\ORM\OptimisticLockException
*/
public function add($name, $size, $type, $metaData, $uniqueName, $collection)
{
$file = new LhFile();
$file->setName($name);
$file->setSize($size);
$file->setType($type);
$file->setMetadata($metaData);
$file->setFileHash($uniqueName);
$file->setCollection($collection);
$this->em->persist($file);
$this->em->flush();
return $file;
}
It seems to me that in the current request from what I can make out that files are not being added to the collection->files array. The database is receiving the correct relationships which is why on the second request it is fine but the act of saving to the database doesn't auto populate the relationships.
I think you need to explicitly add the file to the collection->files, assuming you have getFiles in LhFileCollection you could add after $this->saveFile($file, $collection);:
$collection->getFiles()->add($file);
There are of course a number of ways it can be done but ultimately you need to add the files to the collection->files.
Personally, I would build up the collection adding each file to the files array and only then save the collection. I wouldn't persist and flush on every file because database actions could be expensive. You have cascade on so it should cascade to all files.
I set up an local TYPO3 7.2 environment with xampp.
The installation works fine and everything else too.
At the beginning I installed the FluidTYPO3 site kickstarter distribution because I wanted to work with FLUID there. The distribution created all extensions needed for that (vhs, flux, fluidpages, fluidcontent) and then I created my provider extension with the builder.
It also created 4 pages or 1 page and 3 subpages. As I wanted to rename them I got the following error/exception:
PHP Warning: file_get_contents(): Filename cannot be empty in F:\xampp\htdocs\src\typo3_src-7.2.0\typo3\sysext\fluid\Classes\View\TemplateView.php line 318
I tried to var_dump() that in that file but it didn't help. I get the correct path and filename on other modules but not on the "page"-module where the error appeared. I can not rename, delete or edit the pages.
It seems that something is null there, mh.
Later I saw that the extensions were not for my TYPO3 version 7.2, only for 6.2.99 I think. So I deinstalled all extensions and downloaded the development extensions from Github (https://github.com/FluidTYPO3).
I installed them with an error that my version is too high. The extensions are only for 7.1.99. I thought that it would not be a problem and it should work with it anyway.
As I tested it there was the same error again, again and again.
I don't know where the problem is. Everything works fine on 6.x installations.
Could that be a bug or did I forget something?
PHP Version: 5.6.3 /
TYPO3 Version: 7.2
Would be very nice if anyone knows about the problem or could help me to solve it. I don't know which information is needed. Feel free to ask if something is needed.
<?php
namespace TYPO3\CMS\Fluid\View;
/* *
* This script is backported from the TYPO3 Flow package "TYPO3.Fluid". *
* *
* It is free software; you can redistribute it and/or modify it under *
* the terms of the GNU Lesser General Public License, either version 3 *
* of the License, or (at your option) any later version. *
* *
* The TYPO3 project - inspiring people to share! *
* */
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext;
use TYPO3\CMS\Extbase\Object\ObjectManager;
use TYPO3\CMS\Fluid\Compatibility\TemplateParserBuilder;
use TYPO3\CMS\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3\CMS\Fluid\Fluid;
/**
* The main template view. Should be used as view if you want Fluid Templating
*
* #api
*/
class TemplateView extends AbstractTemplateView {
/**
* Pattern to be resolved for "#templateRoot" in the other patterns.
* Following placeholders are supported:
* - "#packageResourcesPath"
*
* #var string
*/
protected $templateRootPathPattern = '#packageResourcesPath/Private/Templates';
/**
* Pattern to be resolved for "#partialRoot" in the other patterns.
* Following placeholders are supported:
* - "#packageResourcesPath"
*
* #var string
*/
protected $partialRootPathPattern = '#packageResourcesPath/Private/Partials';
/**
* Pattern to be resolved for "#layoutRoot" in the other patterns.
* Following placeholders are supported:
* - "#packageResourcesPath"
*
* #var string
*/
protected $layoutRootPathPattern = '#packageResourcesPath/Private/Layouts';
/**
* Path(s) to the template root. If NULL, then $this->templateRootPathPattern will be used.
*
* #var array
*/
protected $templateRootPaths = NULL;
/**
* Path(s) to the partial root. If NULL, then $this->partialRootPathPattern will be used.
*
* #var array
*/
protected $partialRootPaths = NULL;
/**
* Path(s) to the layout root. If NULL, then $this->layoutRootPathPattern will be used.
*
* #var array
*/
protected $layoutRootPaths = NULL;
/**
* File pattern for resolving the template file
* Following placeholders are supported:
* - "#templateRoot"
* - "#partialRoot"
* - "#layoutRoot"
* - "#subpackage"
* - "#action"
* - "#format"
*
* #var string
*/
protected $templatePathAndFilenamePattern = '#templateRoot/#subpackage/#controller/#action.#format';
/**
* Directory pattern for global partials. Not part of the public API, should not be changed for now.
* Following placeholders are supported:
* - "#templateRoot"
* - "#partialRoot"
* - "#layoutRoot"
* - "#subpackage"
* - "#partial"
* - "#format"
*
* #var string
*/
private $partialPathAndFilenamePattern = '#partialRoot/#subpackage/#partial.#format';
/**
* File pattern for resolving the layout
* Following placeholders are supported:
* - "#templateRoot"
* - "#partialRoot"
* - "#layoutRoot"
* - "#subpackage"
* - "#layout"
* - "#format"
*
* #var string
*/
protected $layoutPathAndFilenamePattern = '#layoutRoot/#layout.#format';
/**
* Path and filename of the template file. If set, overrides the templatePathAndFilenamePattern
*
* #var string
*/
protected $templatePathAndFilename = NULL;
/**
* Path and filename of the layout file. If set, overrides the layoutPathAndFilenamePattern
*
* #var string
*/
protected $layoutPathAndFilename = NULL;
/**
* Constructor
*/
public function __construct() {
$this->templateParser = TemplateParserBuilder::build();
$this->objectManager = GeneralUtility::makeInstance(ObjectManager::class);
$this->setRenderingContext($this->objectManager->get(RenderingContextInterface::class));
}
/**
* Init view
*/
public function initializeView() {
}
// Here, the backporter can insert a constructor method, which is needed for the TYPO3 CMS extension
/**
* Sets the path and name of of the template file. Effectively overrides the
* dynamic resolving of a template file.
*
* #param string $templatePathAndFilename Template file path
* #return void
* #api
*/
public function setTemplatePathAndFilename($templatePathAndFilename) {
$this->templatePathAndFilename = $templatePathAndFilename;
}
/**
* Sets the path and name of the layout file. Overrides the dynamic resolving of the layout file.
*
* #param string $layoutPathAndFilename Path and filename of the layout file
* #return void
* #api
*/
public function setLayoutPathAndFilename($layoutPathAndFilename) {
$this->layoutPathAndFilename = $layoutPathAndFilename;
}
/**
* Set the root path to the templates.
* If set, overrides the one determined from $this->templateRootPathPattern
*
* #param string $templateRootPath Root path to the templates. If set, overrides the one determined from $this->templateRootPathPattern
* #return void
* #api
* #see setTemplateRootPaths()
*/
public function setTemplateRootPath($templateRootPath) {
$this->setTemplateRootPaths(array($templateRootPath));
}
/**
* Resolves the template root to be used inside other paths.
*
* #return array Path(s) to template root directory
*/
public function getTemplateRootPaths() {
if ($this->templateRootPaths !== NULL) {
return $this->templateRootPaths;
}
/** #var $actionRequest \TYPO3\CMS\Extbase\Mvc\Request */
$actionRequest = $this->controllerContext->getRequest();
return array(str_replace('#packageResourcesPath', ExtensionManagementUtility::extPath($actionRequest->getControllerExtensionKey()) . 'Resources/', $this->templateRootPathPattern));
}
/**
* Set the root path(s) to the templates.
* If set, overrides the one determined from $this->templateRootPathPattern
*
* #param array $templateRootPaths Root path(s) to the templates. If set, overrides the one determined from $this->templateRootPathPattern
* #return void
* #api
*/
public function setTemplateRootPaths(array $templateRootPaths) {
$this->templateRootPaths = $templateRootPaths;
}
/**
* Set the root path to the partials.
* If set, overrides the one determined from $this->partialRootPathPattern
*
* #param string $partialRootPath Root path to the partials. If set, overrides the one determined from $this->partialRootPathPattern
* #return void
* #api
* #see setPartialRootPaths()
*/
public function setPartialRootPath($partialRootPath) {
$this->setPartialRootPaths(array($partialRootPath));
}
/**
* Set the root path(s) to the partials.
* If set, overrides the one determined from $this->partialRootPathPattern
*
* #param array $partialRootPaths Root paths to the partials. If set, overrides the one determined from $this->partialRootPathPattern
* #return void
* #api
*/
public function setPartialRootPaths(array $partialRootPaths) {
$this->partialRootPaths = $partialRootPaths;
}
/**
* Resolves the partial root to be used inside other paths.
*
* #return array Path(s) to partial root directory
*/
protected function getPartialRootPaths() {
if ($this->partialRootPaths !== NULL) {
return $this->partialRootPaths;
}
/** #var $actionRequest \TYPO3\CMS\Extbase\Mvc\Request */
$actionRequest = $this->controllerContext->getRequest();
return array(str_replace('#packageResourcesPath', ExtensionManagementUtility::extPath($actionRequest->getControllerExtensionKey()) . 'Resources/', $this->partialRootPathPattern));
}
/**
* Set the root path to the layouts.
* If set, overrides the one determined from $this->layoutRootPathPattern
*
* #param string $layoutRootPath Root path to the layouts. If set, overrides the one determined from $this->layoutRootPathPattern
* #return void
* #api
* #see setLayoutRootPaths()
*/
public function setLayoutRootPath($layoutRootPath) {
$this->setLayoutRootPaths(array($layoutRootPath));
}
/**
* Set the root path(s) to the layouts.
* If set, overrides the one determined from $this->layoutRootPathPattern
*
* #param array $layoutRootPaths Root path to the layouts. If set, overrides the one determined from $this->layoutRootPathPattern
* #return void
* #api
*/
public function setLayoutRootPaths(array $layoutRootPaths) {
$this->layoutRootPaths = $layoutRootPaths;
}
/**
* Resolves the layout root to be used inside other paths.
*
* #return string Path(s) to layout root directory
*/
protected function getLayoutRootPaths() {
if ($this->layoutRootPaths !== NULL) {
return $this->layoutRootPaths;
}
/** #var $actionRequest \TYPO3\CMS\Extbase\Mvc\Request */
$actionRequest = $this->controllerContext->getRequest();
return array(str_replace('#packageResourcesPath', ExtensionManagementUtility::extPath($actionRequest->getControllerExtensionKey()) . 'Resources/', $this->layoutRootPathPattern));
}
/**
* Returns a unique identifier for the resolved template file
* This identifier is based on the template path and last modification date
*
* #param string $actionName Name of the action. If NULL, will be taken from request.
* #return string template identifier
*/
protected function getTemplateIdentifier($actionName = NULL) {
$templatePathAndFilename = $this->getTemplatePathAndFilename($actionName);
if ($actionName === NULL) {
/** #var $actionRequest \TYPO3\CMS\Extbase\Mvc\Request */
$actionRequest = $this->controllerContext->getRequest();
$actionName = $actionRequest->getControllerActionName();
}
$prefix = 'action_' . $actionName;
return $this->createIdentifierForFile($templatePathAndFilename, $prefix);
}
/**
* Resolve the template path and filename for the given action. If $actionName
* is NULL, looks into the current request.
*
* #param string $actionName Name of the action. If NULL, will be taken from request.
* #return string Full path to template
* #throws Exception\InvalidTemplateResourceException
*/
protected function getTemplateSource($actionName = NULL) {
$templatePathAndFilename = $this->getTemplatePathAndFilename($actionName);
$templateSource = file_get_contents($templatePathAndFilename);
if ($templateSource === FALSE) {
throw new Exception\InvalidTemplateResourceException('"' . $templatePathAndFilename . '" is not a valid template resource URI.', 1257246929);
}
return $templateSource;
}
/**
* Resolve the template path and filename for the given action. If $actionName
* is NULL, looks into the current request.
*
* #param string $actionName Name of the action. If NULL, will be taken from request.
* #return string Full path to template
* #throws Exception\InvalidTemplateResourceException
*/
protected function getTemplatePathAndFilename($actionName = NULL) {
if ($this->templatePathAndFilename !== NULL) {
return $this->resolveFileNamePath($this->templatePathAndFilename);
}
if ($actionName === NULL) {
/* #var $actionRequest \TYPO3\CMS\Extbase\Mvc\Request */
$actionRequest = $this->controllerContext->getRequest();
$actionName = $actionRequest->getControllerActionName();
}
$paths = $this->expandGenericPathPattern($this->templatePathAndFilenamePattern, FALSE, FALSE);
$possibleFileNames = $this->buildListOfTemplateCandidates($actionName, $paths, '#action');
foreach ($possibleFileNames as $templatePathAndFilename) {
if ($this->testFileExistence($templatePathAndFilename)) {
return $templatePathAndFilename;
}
}
throw new Exception\InvalidTemplateResourceException('Template could not be loaded. I tried "' . implode('", "', $possibleFileNames) . '"', 1225709595);
}
/**
* Returns a unique identifier for the resolved layout file.
* This identifier is based on the template path and last modification date
*
* #param string $layoutName The name of the layout
* #return string layout identifier
*/
protected function getLayoutIdentifier($layoutName = 'Default') {
$layoutPathAndFilename = $this->getLayoutPathAndFilename($layoutName);
$prefix = 'layout_' . $layoutName;
return $this->createIdentifierForFile($layoutPathAndFilename, $prefix);
}
/**
* Resolve the path and file name of the layout file, based on
* $this->layoutPathAndFilename and $this->layoutPathAndFilenamePattern.
*
* In case a layout has already been set with setLayoutPathAndFilename(),
* this method returns that path, otherwise a path and filename will be
* resolved using the layoutPathAndFilenamePattern.
*
* #param string $layoutName Name of the layout to use. If none given, use "Default"
* #return string contents of the layout template
* #throws Exception\InvalidTemplateResourceException
*/
protected function getLayoutSource($layoutName = 'Default') {
$layoutPathAndFilename = $this->getLayoutPathAndFilename($layoutName);
$layoutSource = file_get_contents($layoutPathAndFilename);
if ($layoutSource === FALSE) {
throw new Exception\InvalidTemplateResourceException('"' . $layoutPathAndFilename . '" is not a valid template resource URI.', 1257246930);
}
return $layoutSource;
}
/**
* Resolve the path and file name of the layout file, based on
* $this->layoutPathAndFilename and $this->layoutPathAndFilenamePattern.
*
* In case a layout has already been set with setLayoutPathAndFilename(),
* this method returns that path, otherwise a path and filename will be
* resolved using the layoutPathAndFilenamePattern.
*
* #param string $layoutName Name of the layout to use. If none given, use "Default"
* #return string Path and filename of layout files
* #throws Exception\InvalidTemplateResourceException
*/
protected function getLayoutPathAndFilename($layoutName = 'Default') {
if ($this->layoutPathAndFilename !== NULL) {
return $this->resolveFileNamePath($this->layoutPathAndFilename);
}
$paths = $this->expandGenericPathPattern($this->layoutPathAndFilenamePattern, TRUE, TRUE);
$possibleFileNames = $this->buildListOfTemplateCandidates($layoutName, $paths, '#layout');
foreach ($possibleFileNames as $layoutPathAndFilename) {
if ($this->testFileExistence($layoutPathAndFilename)) {
return $layoutPathAndFilename;
}
}
throw new Exception\InvalidTemplateResourceException('The layout files "' . implode('", "', $possibleFileNames) . '" could not be loaded.', 1225709596);
}
/**
* Returns a unique identifier for the resolved partial file.
* This identifier is based on the template path and last modification date
*
* #param string $partialName The name of the partial
* #return string partial identifier
*/
protected function getPartialIdentifier($partialName) {
$partialPathAndFilename = $this->getPartialPathAndFilename($partialName);
$prefix = 'partial_' . $partialName;
return $this->createIdentifierForFile($partialPathAndFilename, $prefix);
}
/**
* Figures out which partial to use.
*
* #param string $partialName The name of the partial
* #return string contents of the partial template
* #throws Exception\InvalidTemplateResourceException
*/
protected function getPartialSource($partialName) {
$partialPathAndFilename = $this->getPartialPathAndFilename($partialName);
$partialSource = file_get_contents($partialPathAndFilename);
if ($partialSource === FALSE) {
throw new Exception\InvalidTemplateResourceException('"' . $partialPathAndFilename . '" is not a valid template resource URI.', 1257246931);
}
return $partialSource;
}
/**
* Resolve the partial path and filename based on $this->partialPathAndFilenamePattern.
*
* #param string $partialName The name of the partial
* #return string the full path which should be used. The path definitely exists.
* #throws Exception\InvalidTemplateResourceException
*/
protected function getPartialPathAndFilename($partialName) {
$paths = $this->expandGenericPathPattern($this->partialPathAndFilenamePattern, TRUE, TRUE);
$possibleFileNames = $this->buildListOfTemplateCandidates($partialName, $paths, '#partial');
foreach ($possibleFileNames as $partialPathAndFilename) {
if ($this->testFileExistence($partialPathAndFilename)) {
return $partialPathAndFilename;
}
}
throw new Exception\InvalidTemplateResourceException('The partial files "' . implode('", "', $possibleFileNames) . '" could not be loaded.', 1225709597);
}
/**
* Builds a list of possible candidates for a given template name
*
* #param string $templateName
* #param array $paths Paths to search in
* #param string $marker Marker to replace in the $templateName
* #return array Array of paths to search for the template file
*/
protected function buildListOfTemplateCandidates($templateName, $paths, $marker) {
$upperCasedTemplateName = $this->ucFileNameInPath($templateName);
$possibleFileNames = array();
foreach ($paths as $partialPathAndFilename) {
$possibleFileNames[] = $this->resolveFileNamePath(str_replace($marker, $upperCasedTemplateName, $partialPathAndFilename));
if ($templateName !== $upperCasedTemplateName) {
$possibleFileNames[] = $this->resolveFileNamePath(str_replace($marker, $templateName, $partialPathAndFilename));
}
}
return $possibleFileNames;
}
/**
* Checks whether a template can be resolved for the current request context.
*
* #param ControllerContext $controllerContext Controller context which is available inside the view
* #return bool
* #api
*/
public function canRender(ControllerContext $controllerContext) {
$this->setControllerContext($controllerContext);
try {
$this->getTemplateSource();
return TRUE;
} catch (Exception\InvalidTemplateResourceException $e) {
return FALSE;
}
}
/**
* Processes following placeholders inside $pattern:
* - "#templateRoot"
* - "#partialRoot"
* - "#layoutRoot"
* - "#subpackage"
* - "#controller"
* - "#format"
*
* This method is used to generate "fallback chains" for file system locations where a certain Partial can reside.
*
* If $bubbleControllerAndSubpackage is FALSE and $formatIsOptional is FALSE, then the resulting array will only have one element
* with all the above placeholders replaced.
*
* If you set $bubbleControllerAndSubpackage to TRUE, then you will get an array with potentially many elements:
* The first element of the array is like above. The second element has the # controller part set to "" (the empty string)
* The third element now has the # controller part again stripped off, and has the last subpackage part stripped off as well.
* This continues until both "#subpackage" and "#controller" are empty.
*
* Example for $bubbleControllerAndSubpackage is TRUE, we have the Tx_MyExtension_MySubPackage_Controller_MyController
* as Controller Object Name and the current format is "html"
*
* If pattern is "#templateRoot/#subpackage/#controller/#action.#format", then the resulting array is:
* - "Resources/Private/Templates/MySubPackage/My/#action.html"
* - "Resources/Private/Templates/MySubPackage/#action.html"
* - "Resources/Private/Templates/#action.html"
*
* If you set $formatIsOptional to TRUE, then for any of the above arrays, every element will be duplicated - once with "#format"
* replaced by the current request format, and once with ."#format" stripped off.
*
* #param string $pattern Pattern to be resolved
* #param bool $bubbleControllerAndSubpackage if TRUE, then we successively split off parts from "#controller" and "#subpackage" until both are empty.
* #param bool $formatIsOptional if TRUE, then half of the resulting strings will have ."#format" stripped off, and the other half will have it.
* #return array unix style paths
*/
protected function expandGenericPathPattern($pattern, $bubbleControllerAndSubpackage, $formatIsOptional) {
$paths = array($pattern);
$this->expandPatterns($paths, '#templateRoot', $this->getTemplateRootPaths());
$this->expandPatterns($paths, '#partialRoot', $this->getPartialRootPaths());
$this->expandPatterns($paths, '#layoutRoot', $this->getLayoutRootPaths());
/** #var $actionRequest \TYPO3\CMS\Extbase\Mvc\Request */
$actionRequest = $this->controllerContext->getRequest();
$subpackageKey = $actionRequest->getControllerSubpackageKey();
$controllerName = $actionRequest->getControllerName();
if ($subpackageKey !== NULL) {
if (strpos($subpackageKey, Fluid::NAMESPACE_SEPARATOR) !== FALSE) {
$namespaceSeparator = Fluid::NAMESPACE_SEPARATOR;
} else {
$namespaceSeparator = Fluid::LEGACY_NAMESPACE_SEPARATOR;
}
$subpackageKeyParts = explode($namespaceSeparator, $subpackageKey);
} else {
$subpackageKeyParts = array();
}
if ($bubbleControllerAndSubpackage) {
$numberOfPathsBeforeSubpackageExpansion = count($paths);
$numberOfSubpackageParts = count($subpackageKeyParts);
$subpackageReplacements = array();
for ($i = 0; $i <= $numberOfSubpackageParts; $i++) {
$subpackageReplacements[] = implode('/', ($i < 0 ? $subpackageKeyParts : array_slice($subpackageKeyParts, $i)));
}
$this->expandPatterns($paths, '#subpackage', $subpackageReplacements);
for ($i = ($numberOfPathsBeforeSubpackageExpansion - 1) * ($numberOfSubpackageParts + 1); $i >= 0; $i -= ($numberOfSubpackageParts + 1)) {
array_splice($paths, $i, 0, str_replace('#controller', $controllerName, $paths[$i]));
}
$this->expandPatterns($paths, '#controller', array(''));
} else {
$i = $controllerName === NULL ? 0 : -1;
$this->expandPatterns($paths, '#subpackage', array(implode('/', $i < 0 ? $subpackageKeyParts :
array_slice($subpackageKeyParts, $i))));
$this->expandPatterns($paths, '#controller', array($controllerName));
}
if ($formatIsOptional) {
$this->expandPatterns($paths, '.#format', array('.' . $actionRequest->getFormat(), ''));
$this->expandPatterns($paths, '#format', array($actionRequest->getFormat(), ''));
} else {
$this->expandPatterns($paths, '.#format', array('.' . $actionRequest->getFormat()));
$this->expandPatterns($paths, '#format', array($actionRequest->getFormat()));
}
return array_values(array_unique($paths));
}
/**
* Expands the given $patterns by adding an array element for each $replacement
* replacing occurrences of $search.
*
* #param array $patterns
* #param string $search
* #param array $replacements
* #return void
*/
protected function expandPatterns(array &$patterns, $search, array $replacements) {
$patternsWithReplacements = array();
foreach ($patterns as $pattern) {
foreach ($replacements as $replacement) {
$patternsWithReplacements[] = GeneralUtility::fixWindowsFilePath(str_replace($search, $replacement, $pattern));
}
}
$patterns = $patternsWithReplacements;
}
/**
* Returns a unique identifier for the given file in the format
* <PackageKey>_<SubPackageKey>_<ControllerName>_<prefix>_<SHA1>
* The SH1 hash is a checksum that is based on the file path and last modification date
*
* #param string $pathAndFilename
* #param string $prefix
* #return string
*/
protected function createIdentifierForFile($pathAndFilename, $prefix) {
/** #var $actionRequest \TYPO3\CMS\Extbase\Mvc\Request */
$actionRequest = $this->controllerContext->getRequest();
$extensionName = $actionRequest->getControllerExtensionName();
$subPackageKey = $actionRequest->getControllerSubpackageKey();
if ($subPackageKey !== NULL) {
$extensionName .= '_' . $subPackageKey;
}
$controllerName = $actionRequest->getControllerName();
$templateModifiedTimestamp = filemtime($pathAndFilename);
$templateIdentifier = sprintf('%s_%s_%s_%s', $extensionName, $controllerName, $prefix, sha1($pathAndFilename . '|' . $templateModifiedTimestamp));
return $templateIdentifier;
}
/**
* Wrapper method to make the static call to GeneralUtility mockable in tests
*
* #param string $pathAndFilename
*
* #return string absolute pathAndFilename
*/
protected function resolveFileNamePath($pathAndFilename) {
return GeneralUtility::getFileAbsFileName(GeneralUtility::fixWindowsFilePath($pathAndFilename), TRUE);
}
}
$templatePathAndFilename = $this->getTemplatePathAndFilename($actionName);
This is the bad one in the function "getTemplateSource".
I downgraded the php version to 5.5 (new xampp installation) and it didn't work. :-(
Hoping for help,
thanks!
Okay I think the problem is solved.
The problem was that the column "tx_fluidpages_layout" of the table "pages" in the database had no entry. It was NULL.
Because I could not change and save it in the page properties without calling that error I changed it with phpmyadmin.
To fix that error you have to manually go to the table "pages" in the database f.e. with phpmyadmin and then to the column "tx_fluidpages_layout". There you have to edit the value and change it to "fluidpages__fluidpages". After that you can save it and reload the backend.
Now you should edit the page properties and set your page layout which comes from your provider extension.
I think it is a bug anyway, that should not happened. In TYPO3 6.x it was inserted automatically.
I've made a simple symfony2 console script which is supposed to convert data from old model to the new one.
Here's what it looks like:
class ConvertScreenshotsCommand extends Command
{
[...]
protected function execute(InputInterface $input, OutputInterface $output)
{
$em = $this->getContainer()->get('doctrine')->getManager();
$output->writeln('<info>Conversion started on ' . date(DATE_RSS) . "</info>");
$output->writeln('Getting all reviews...');
$reviews = $em->getRepository('ACCommonBundle:Review')->findAll(); // Putting all Review entities into an array
$output->writeln('<info>Got ' . count($reviews) . ' reviews.</info>');
foreach ($reviews as $review) {
$output->writeln("<info>Screenshots for " . $review->getTitle() . "</info>");
if ($review->getLegacyScreenshots()) {
foreach ($review->getLegacyScreenshots() as $filename) { // fn returns array of strings
$output->writeln("Found " . $filename);
$screenshot = new ReviewScreenshot(); // new object
$screenshot->setReview($review); // review is object
$screenshot->setFilename($filename); // filename is string
$em->persist($screenshot);
$em->flush(); // this is where it dies
$output->writeln("Successfully added to the database.");
}
} else $output->writeln("No legacy screenshots found.");
}
$output->writeln('<info>Conversion ended on ' . date(DATE_RSS) . "</info>");
}
}
The script breaks on $em->flush(), with the following error:
[ErrorException]
Warning: spl_object_hash() expects parameter 1 to be object, string given in
/[...]/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php line 1324
Obviously I'm doing something wrong, but can't figure out what it is. Thanks in advance!
** Update **
Review Entity mapping:
class Review
{
[...]
/**
* #ORM\OneToMany(targetEntity="ReviewScreenshot", mappedBy="review")
*/
protected $screenshots;
/**
* Won't be stored in the DB
* #deprecated
*/
private $legacyScreenshots;
/**
* New method to get screenshots, currently calls old method for the sake of compatibility
* #return array Screenshot paths
*/
public function getScreenshots()
{
// return $this->getLegacyScreenshots(); // Old method
return $this->screenshots; // New method
}
/**
* Get Screenshot paths
* #return array Screenshot paths
* #deprecated
*/
public function getLegacyScreenshots()
{
$dir=$this->getUploadRootDir();
if (file_exists($dir)) {
$fileList = scandir($dir);
$this->screenshots = array();
foreach ($fileList as $fileName)
{
preg_match("/(screenshot-\d+.*)/", $fileName, $matches);
if ($matches)
$this->screenshots[]=$matches[1];
}
return $this->screenshots;
}
else return null;
}
ReviewScreenshot mapping:
class ReviewScreenshot
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string $filename
*
* #ORM\Column(name="filename", type="string", length=255)
*/
private $filename;
/**
* #ORM\ManyToOne(targetEntity="Review", inversedBy="screenshots")
* #ORM\JoinColumn(name="review_id", referencedColumnName="id")
*/
protected $review;
/**
* #var integer $priority
*
* #ORM\Column(name="priority", type="integer", nullable=true)
*/
protected $priority;
/**
* #var string $description
*
* #ORM\Column(name="description", type="string", nullable=true)
*/
protected $description;
/**
* #Assert\File(maxSize="2097152")
*/
public $screenshot_file;
protected $webPath;
UnitOfWork.php
/**
* Gets the state of an entity with regard to the current unit of work.
*
* #param object $entity
* #param integer $assume The state to assume if the state is not yet known (not MANAGED or REMOVED).
* This parameter can be set to improve performance of entity state detection
* by potentially avoiding a database lookup if the distinction between NEW and DETACHED
* is either known or does not matter for the caller of the method.
* #return int The entity state.
*/
public function getEntityState($entity, $assume = null)
{
$oid = spl_object_hash($entity); // <-- Line 1324
if (isset($this->entityStates[$oid])) {
return $this->entityStates[$oid];
}
if ($assume !== null) {
return $assume;
}
// State can only be NEW or DETACHED, because MANAGED/REMOVED states are known.
// Note that you can not remember the NEW or DETACHED state in _entityStates since
// the UoW does not hold references to such objects and the object hash can be reused.
// More generally because the state may "change" between NEW/DETACHED without the UoW being aware of it.
$class = $this->em->getClassMetadata(get_class($entity));
$id = $class->getIdentifierValues($entity);
if ( ! $id) {
return self::STATE_NEW;
}
switch (true) {
case ($class->isIdentifierNatural());
// Check for a version field, if available, to avoid a db lookup.
if ($class->isVersioned) {
return ($class->getFieldValue($entity, $class->versionField))
? self::STATE_DETACHED
: self::STATE_NEW;
}
// Last try before db lookup: check the identity map.
if ($this->tryGetById($id, $class->rootEntityName)) {
return self::STATE_DETACHED;
}
// db lookup
if ($this->getEntityPersister($class->name)->exists($entity)) {
return self::STATE_DETACHED;
}
return self::STATE_NEW;
case ( ! $class->idGenerator->isPostInsertGenerator()):
// if we have a pre insert generator we can't be sure that having an id
// really means that the entity exists. We have to verify this through
// the last resort: a db lookup
// Last try before db lookup: check the identity map.
if ($this->tryGetById($id, $class->rootEntityName)) {
return self::STATE_DETACHED;
}
// db lookup
if ($this->getEntityPersister($class->name)->exists($entity)) {
return self::STATE_DETACHED;
}
return self::STATE_NEW;
default:
return self::STATE_DETACHED;
}
}
I think the problem lies within Review::$screenshots:
You map it as a OneToMany association, so the value should be a Collection of ReviewScreenshot entities. But the method Review::getLegacyScreenshots() will change it into an array of strings.
You're probably using the change-tracking policy DEFERRED_IMPLICIT (which is the default). So when the property Review::$screenshots changes, Doctrine will try to persist that change, encounters strings where it expects entities, so throws the exception.
I am trying to change the layout content in my controller, which only seems to overwrite it, only append.
I have built a CMS using Zend and Smarty. I have one main layout with, which displays the content for each page:
{$this->layout()->content}
Although when I try to overwrite the 'content' in the controller with a new content area this causes both the index/index.tpl and contact/contact.tpl to be displayed:
$this->view->content = $this->view->display('contact/contact.tpl');
I know I could manually assign the content to a view smarty variable, although I would like to reduce the assigns in smarty and use Zend.
In my application.ini
smarty.caching = 1
smarty.cache_lifetime = 14400
smarty.template_dir = PATH "/templates/default/"
smarty.compile_dir = PATH "/tmp/smarty_compile/"
smarty.plugins_dir = APPLICATION_PATH "/plugins/smarty/"
smarty.config_dir = ""
smarty.cache_dir = PATH "/tmp/smarty_cache/"
smarty.left_delimiter = "{"
smarty.right_delimiter = "}"
In my bootstrap.php
protected function _initView()
{
$view = new Web_View_Smarty($this->getOption('smarty'));
$viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('ViewRenderer');
$viewRenderer->setViewSuffix('tpl');
$viewRenderer->setView($view);
$this->bootstrap('layout');
$layout = Zend_Layout::getMvcInstance();
$layout->setViewSuffix('tpl');
return $view;
}
In my Smarty.php
<?php
/**
* Smarty template engine integration into Zend Framework
*/
class Web_View_Smarty extends Zend_View_Abstract
{
/**
* Instance of Smarty
* #var Smarty
*/
protected $_smarty = null;
/**
* Template explicitly set to render in this view
* #var string
*/
protected $_customTemplate = '';
/**
* Smarty config
* #var array
*/
private $_config = null;
/**
* Class definition and constructor
*
* Let's start with the class definition and the constructor part. My class Travello_View_Smarty is extending the Zend_View_Abstract class. In the constructor the parent constructor from Zend_View_Abstract is called first. After that a Smarty object is instantiated, configured and stored in a private attribute.
* Please note that I use a configuration object from the object store to get the configuration data for Smarty.
*
* #param array $smartyConfig
* #param array $config
*/
public function __construct($smartyConfig, $config = array())
{
$this->_config = $smartyConfig;
parent::__construct($config);
$this->_loadSmarty();
}
/**
* Return the template engine object
*
* #return Smarty
*/
public function getEngine()
{
return $this->_smarty;
}
/**
* Implement _run() method
*
* The method _run() is the only method that needs to be implemented in any subclass of Zend_View_Abstract. It is called automatically within the render() method. My implementation just uses the display() method from Smarty to generate and output the template.
*
* #param string $template
*/
protected function _run()
{
$file = func_num_args() > 0 && file_exists(func_get_arg(0)) ? func_get_arg(0) : '';
if ($this->_customTemplate || $file) {
$template = $this->_customTemplate;
if (!$template) {
$template = $file;
}
$this->_smarty->display($template);
} else {
throw new Zend_View_Exception('Cannot render view without any template being assigned or file does not exist');
}
}
/**
* Overwrite assign() method
*
* The next part is an overwrite of the assign() method from Zend_View_Abstract, which works in a similar way. The big difference is that the values are assigned to the Smarty object and not to the $this->_vars variables array of Zend_View_Abstract.
*
* #param string|array $var
* #return Ext_View_Smarty
*/
public function assign($var, $value = null)
{
if (is_string($var)) {
$this->_smarty->assign($var, $value);
} elseif (is_array($var)) {
foreach ($var as $key => $value) {
$this->assign($key, $value);
}
} else {
throw new Zend_View_Exception('assign() expects a string or array, got '.gettype($var));
}
return $this;
}
public function display($template){
$this->clearVars();
$this->_smarty->display($template);
return $this;
}
/**
* Overwrite escape() method
*
* The next part is an overwrite of the escape() method from Zend_View_Abstract. It works both for string and array values and also uses the escape() method from the Zend_View_Abstract. The advantage of this is that I don't have to care about each value of an array to get properly escaped.
*
* #param mixed $var
* #return mixed
*/
public function escape($var)
{
if (is_string($var)) {
return parent::escape($var);
} elseif (is_array($var)) {
foreach ($var as $key => $val) {
$var[$key] = $this->escape($val);
}
}
return $var;
}
/**
* Print the output
*
* The next method output() is a wrapper on the render() method from Zend_View_Abstract. It just sets some headers before printing the output.
*
* #param <type> $name
*/
public function output($name)
{
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Cache-Control: no-cache");
header("Pragma: no-cache");
header("Cache-Control: post-check=0, pre-check=0", false);
print parent::render($name);
}
/**
* Use Smarty caching
*
* The last two methods were created to simply integrate the Smarty caching mechanism in the View class. With the first one you can check for cached template and with the second one you can set the caching on or of.
*
* #param string $template
* #return bool
*/
public function isCached($template)
{
return $this->_smarty->is_cached($template);
}
/**
* Enable/disable caching
*
* #param bool $caching
* #return Ext_View_Smarty
*/
public function setCaching($caching)
{
$this->_smarty->caching = $caching;
return $this;
}
/**
* Template getter (return file path)
* #return string
*/
public function getTemplate()
{
return $this->_customTemplate;
}
/**
* Template filename setter
* #param string
* #return Ext_View_Smarty
*/
public function setTemplate($tpl)
{
$this->_customTemplate = $tpl;
return $this;
}
/**
* Magic setter for Zend_View compatibility. Performs assign()
*
* #param string $key
* #param mixed $val
*/
public function __set($key, $val)
{
$this->assign($key, $val);
}
/**
* Magic getter for Zend_View compatibility. Retrieves template var
*
* #param string $key
* #return mixed
*/
public function __get($key)
{
return $this->_smarty->getTemplateVars($key);
}
/**
* Magic getter for Zend_View compatibility. Removes template var
*
* #see View/Zend_View_Abstract::__unset()
* #param string $key
*/
public function __unset($key)
{
$this->_smarty->clearAssign($key);
}
/**
* Allows testing with empty() and isset() to work
* Zend_View compatibility. Checks template var for existance
*
* #param string $key
* #return boolean
*/
public function __isset($key)
{
return (null !== $this->_smarty->getTemplateVars($key));
}
/**
* Zend_View compatibility. Retrieves all template vars
*
* #see Zend_View_Abstract::getVars()
* #return array
*/
public function getVars()
{
return $this->_smarty->getTemplateVars();
}
/**
* Updates Smarty's template_dir field with new value
*
* #param string $dir
* #return Ext_View_Smarty
*/
public function setTemplateDir($dir)
{
$this->_smarty->setTemplateDir($dir);
return $this;
}
/**
* Adds another Smarty template_dir to scan for templates
*
* #param string $dir
* #return Ext_View_Smarty
*/
public function addTemplateDir($dir)
{
$this->_smarty->addTemplateDir($dir);
return $this;
}
/**
* Adds another Smarty plugin directory to scan for plugins
*
* #param string $dir
* #return Ext_View_Smarty
*/
public function addPluginDir($dir)
{
$this->_smarty->addPluginsDir($dir);
return $this;
}
/**
* Zend_View compatibility. Removes all template vars
*
* #see View/Zend_View_Abstract::clearVars()
* #return Ext_View_Smarty
*/
public function clearVars()
{
$this->_smarty->clearAllAssign();
$this->assign('this', $this);
return $this;
}
/**
* Zend_View compatibility. Add the templates dir
*
* #see View/Zend_View_Abstract::addBasePath()
* #return Ext_View_Smarty
*/
public function addBasePath($path, $classPrefix = 'Zend_View')
{
parent::addBasePath($path, $classPrefix);
$this->addScriptPath(PATH . '/templates/default');
$this->addTemplateDir(PATH . '/templates/shared');
return $this;
}
/**
* Zend_View compatibility. Set the templates dir instead of scripts
*
* #see View/Zend_View_Abstract::setBasePath()
* #return Ext_View_Smarty
*/
public function setBasePath($path, $classPrefix = 'Zend_View')
{
parent::setBasePath($path, $classPrefix);
$this->setScriptPath(PATH . '/templates/default');
$this->addTemplateDir(PATH . '/templates/shared');
return $this;
}
/**
* Magic clone method, on clone create diferent smarty object
*/
public function __clone() {
$this->_loadSmarty();
}
/**
* Initializes the smarty and populates config params
*
* #throws Zend_View_Exception
* #return void
*/
private function _loadSmarty()
{
if (!class_exists('Smarty', true)) {
require_once 'Smarty/Smarty.class.php';
}
$this->_smarty = new Smarty();
if ($this->_config === null) {
throw new Zend_View_Exception("Could not locate Smarty config - node 'smarty' not found");
}
$this->_smarty->caching = $this->_config['caching'];
$this->_smarty->cache_lifetime = $this->_config['cache_lifetime'];
$this->_smarty->template_dir = $this->_config['template_dir'];
$this->_smarty->compile_dir = $this->_config['compile_dir'];
$this->_smarty->config_dir = $this->_config['config_dir'];
$this->_smarty->plugins_dir = $this->_config['plugins_dir'];
$this->_smarty->cache_dir = $this->_config['cache_dir'];
$this->_smarty->left_delimiter = $this->_config['left_delimiter'];
$this->_smarty->right_delimiter = $this->_config['right_delimiter'];
$this->assign('this', $this);
}
}
There is Smarty implementation in Zend Documentation.
Smarty is bad, any template engine is bad and slowing down your application.
Use zend native views.
And remember that PHP is was invented to be a template engine.
http://framework.zend.com/manual/1.11/en/zend.view.scripts.html
When in your template, you write
{$this->layout()->content}
This is the content of the layout helper, that will render the view associated to each action.
When, in you controller, you write
$this->view->content = $this->view->display('contact/contact.tpl');
You assign a variable to your view, that can be display, using
{$content}
in your smarty template.
I think you have some confusion in your way of sorting this out. Have a look at
http://framework.zend.com/manual/1.12/en/zend.layout.quickstart.html
Anyway, your question is old and you already have selected an answer as correct, which was not really an answer by the way, but I couldn't pass by and say nothing. Using Smarty with Zend is great, Smarty is faster and compiling/caching have better fine tuning than Zend and the integration between both is neat (thanks to dependency injection). You can have the power of both libraries in one application, so go for it.