I just start to develop a new extension, where one image is included.
If I using the add function from repository the image will be correct saved.
But when I using the edit controller function to change the image file und use than the repository update function, the old image will be not overwritten, but an additional image will be added.
I also tried to use the combination of remove/add instead of update. The result was the same. This I absolutly don't understand!
I am using Typo3 8.7.9. and have used the Extension-Builder, the version in the git.
Can anybody give me a hint, where the problem could be?
You need to remove first FAL object. You can do this like below.
in your controller file You create function for remove old image Fal object.
/**
* remove ImageObject
*
* #param \vendor\ExtensionKey\Domain\Model\Modelname $object
* #return void
*/
public function removeImageObject(\vendor\ExtensionKey\Domain\Model\Modelname $object) {
if(!empty($object->getImage())){
$resourceFactory = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Resource\\ResourceFactory');
$fileReferenceObject = $resourceFactory->getFileReferenceObject($events->getImage()->getUid());
$fileWasDeleted = $fileReferenceObject->getOriginalFile()->delete();
}
}
I understand, that I have first to delete the Filereference (sys_file_reference). From my Recipe class (\TYPO3\CMS\Extbase\DomainObject\AbstractEntity) I already get the filereference object. Therefore I should able to use it to delete it.
I have added the following function.
/**
* Deletes the recipePic Filereference
*
* #param \TYPO3\CMS\Extbase\Domain\Model\FileReference $recipePic
* #return void
*/
public function delRecipePic(\TYPO3\CMS\Extbase\Domain\Model\FileReference $recipePic)
{
$this->recipePic->getOriginalFile()->delete();
$this->recipePic->delete();
}
But both calls are not working. What I am doing wrong?
Related
I have a function in my FeatureContext.php that uses #AfterScenario to clean up fake database entries created during the test. I'd like to add a #debug tag to a particular scenario tell the function to NOT delete the entries created for that scenario if the tag is present.
/**
* Deletes the records created during the scenarios.
* #AfterScenario
*/
public function cleanDB(AfterScenarioScope $scope)
{
// if !#debug present
// delete files from database
// end if
}
#lauda's answer got me close and I figured out the rest.
I used the hasTag() function of Behat's scenario object.
/**
* Deletes the NCP records created during the scenarios.
* #AfterScenario
*/
public function cleanDB(AfterScenarioScope $scope)
{
// if the #debug tag is set, ignore
if (!$scope->getScenario()->hasTag("debug")) {
// delete records from database
}
}
If I decorate the scenario with #debug, I can test for that and change my functionality.
#debug
Scenario: do the thing
...
in the Docs
http://symfony.com/doc/master/cookbook/profiler/storage.html
you still can find Information about Profiler Storage.
I just checked the code and could not find any clues how to set a custom storage.
I also find no Documentation stating this except some #legacy notes in the Original Source at 2.8.
Is there a Reason why this was removed?
I was using redis to store this data with a lifetime of eta 1hour.
Now I need to run a manual cleanup to whipe all files in that directory.
If anybody has some clues or hints on helping me with this issue are appreceated ^^
Chris
Thanks to the Tip of Matteo I was able to solve this quite flexible.
The Team of Symfony removed this, because it was hard coded into the Profiler Subsystem.
Instead of fixing this by adding a class parameter I had to solve it. :)
Ok, here is the code, If somebody needs this to.
First of all we need the Original Classes from Symfony 2.7 (at least I reused them as I only need the Redis Option ( I use it, because I can Compress the data using igbinary)
Next you need to implement a Compiler Pass.
namespace AcmeBunlde\DependencyInjection\CompilerPass;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class ProfilerCompilerPass implements CompilerPassInterface
{
/**
* You can modify the container here before it is dumped to PHP code.
*
* #param ContainerBuilder $container
*/
public function process(ContainerBuilder $container)
{
$definition = $container->getDefinition('profiler');
$definition->addArgument('%acmebundle.profiler.defaultEnabled%');
$definition->addArgument('%acmebundle.profiler.class%');
$definition->addArgument('%acmebundle.profiler.dsn%');
$definition->addArgument('%acmebundle.profiler.username%');
$definition->addArgument('%acmebundle.profiler.password%');
$definition->addArgument('%acmebundle.profiler.ttl%');
$definition->setClass('acmebundle\Profiler\Profiler');
}
}
This needs to be loaded inside the Bundle Loader:
public function build(ContainerBuilder $container)
{
...
$container->addCompilerPass(new ProfilerCompilerPass());
}
After this we need to add the Configuration for the New Profiler Storage in the DependencyInjection Folder.
namespace AcmeBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
/**
* This is the class that validates and merges configuration from your app/config files
*
* To learn more see {#link http://symfony.com/doc/current/cookbook/bundles/extension.html#cookbook-bundles-extension-config-class}
* #author Chris
*/
class Configuration implements ConfigurationInterface
/**
* {#inheritdoc}
*/
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('library');
$rootNode
->children()
->arrayNode('profiler')
->addDefaultsIfNotSet()
->children()
->booleanNode('defaultStorage')
->defaultTrue()
->end()
->scalarNode('class')
->defaultValue('')
->end()
->scalarNode('dsn')
->defaultValue('')
->end()
->scalarNode('username')
->defaultValue('')
->end()
->scalarNode('password')
->defaultValue('')
->end()
->scalarNode('ttl')
->defaultValue('3600')
->end()
->end()
->end();
return $treeBuilder();
}
}
Now set the Default Values in The Dependency Injection Bundle Loader
<?php
namespace AcmeBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
/**
* This is the class that loads and manages your bundle configuration
*
* To learn more see {#link http://symfony.com/doc/current/cookbook/bundles/extension.html}
* #author Chris
*/
class AcmeExtension extends Extension
{
/**
* {#inheritdoc}
*/
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
...
$container->setParameter('acmebundle.profiler.defaultEnabled',$config['profiler']['defaultStorage']);
$container->setParameter('acmebundle.profiler.class',$config['profiler']['class']);
$container->setParameter('acmebundle.profiler.dsn',$config['profiler']['dsn']);
$container->setParameter('acmebundle.profiler.username',$config['profiler']['username']);
$container->setParameter('acmebundle.profiler.password',$config['profiler']['password']);
$container->setParameter('acmebundle.profiler.ttl',$config['profiler']['ttl']);
...
}
...
}
As Last Step you need to build a basic container for adding the new Profiler Handler.
I have choosen to implement it not to complex:
<?php
namespace AcmeBundle\Profiler;
use Psr\Log\LoggerInterface;
use \Symfony\Component\HttpKernel\Profiler\Profiler as ProfilerSrc;
use Symfony\Component\HttpKernel\Profiler\ProfilerStorageInterface;
/**
* Profiler.
*/
class Profiler extends ProfilerSrc
{
public function __construct(ProfilerStorageInterface $storage, LoggerInterface $logger, $defaultEnabled=true,$class=null,$dsn=null,$username=null,$password=null,$ttl=3600)
{
if($defaultEnabled!==true)
{
$storage = new $class($dsn,$username,$password,$ttl);
}
parent::__construct($storage , $logger);
}
}
I have also added a Library to define the Constructor of the Storage Interface.
<?php
namespace AcmeBundle\Profiler;
use Symfony\Component\HttpKernel\Profiler\ProfilerStorageInterface as ProfilerStorageSource;
interface ProfilerStorageInterface extends ProfilerStorageSource
{
/**
* ProfilerStorageInterface constructor.
*
* #param $dsn
* #param $username
* #param $password
* #param $ttl
*/
public function __construct($dsn,$username,$password,$ttl);
}
All you need to do now is to define some Options in your config_dev.yml file.
acmebundle:
profiler:
defaultEnabled: false
class:CLASSNAME INCLUDING NAMESPACE
dsn: redis://localhost/1
username:
password
ttl: 3600
with defaultEnabled = true you can reenable to Original Handler.
the rest is, I believe self explaining.
username + password is from the original feature set.
(ttl == lifetime)
I hope this helps somebody else as well :)
Is marked as deprecated since 2.8 with the suppression in the 3.0. I can't find any motivation about in the PR. The doc is not yet updated as you mention.
The only suggestion is about a comment in this issue:
If you want to use your own implementation of a profiler storage,
then just override the profile.storage service.
Hope this help
I am trying to run a simple query off of the Tx_Extbase_Domain_Repository_FrontendUserRepository. I cannot get anything to work except findByUid(), not even findAll().
In my controller I have this code which seems to work:
/**
* #var Tx_Extbase_Domain_Repository_FrontendUserRepository
*/
protected $userRepository;
/**
* Inject the user repository
* #param Tx_Extbase_Domain_Repository_FrontendUserRepository $userRepository
* #return void */
public function injectFrontendUserRepository(Tx_Extbase_Domain_Repository_FrontendUserRepository $userRepository) {
$this->userRepository = $userRepository;
}
/**
* action create
*
* #param Tx_BpsCoupons_Domain_Model_Coupon $newCoupon
* #return void
*/
public function createAction(Tx_BpsCoupons_Domain_Model_Coupon $newCoupon) {
...... some code .....
$user = $this->userRepository->findByUid(($GLOBALS['TSFE']->fe_user->user[uid]));
$newCoupon->setCreator($user);
...... some code .....
}
but in another function I want to look up a user not by uid but by a fe_users column called vipnumber (an int column) so I tried
/**
* check to see if there is already a user with this vip number in the database
* #param string $vip
* #return bool
*/
public function isVipValid($vip) {
echo "<br/>" . __FUNCTION__ . __LINE__ . "<br/>";
echo "<br/>".$vip."<br/>";
//$ret = $this->userRepository->findByUid(15); //this works!! but
$query = $this->userRepository->createQuery();
$query->matching($query->equals('vip',$vip) );
$ret = $query->execute(); //no luck
.................
and neither does this
$ret = $this->userRepository->findAll();
How can one work but not the others? In my setup I already put
config.tx_extbase.persistence.classes.Tx_Extbase_Domain_Model_FrontendUser.mapping.recordType >
which seems to be necessary for the fiondByUid to work, is i t preventing the other from working?
I am using typo3 v 4.5.30 with extbase 1.3
Thanks
If $this->userRepository->findByUid(15); works, there is no reason why $this->userRepository->findAll(); should not. However $this->userRepository->findAll(); returns not a single Object but a collection of all objects, so you have to iterate over them.
If you add a column to the fe_users, you have to add it to TCA and to your extbase model (you need a getter and a setter), too! After that you can call findByProperty($property) in your repository. In your case that would be
$user = $this->userRepository->findByVipnumber($vip);
This will return all UserObjects that have $vip set as their Vipnumber. If you just want to check if that $vip is already in use, you can call
$user = $this->userRepository->countByVipnumber($vip);
instead. Which obviously returns the number of Users that have this $vip;
You never use $query = $this->createQuery(); outside your Repository.
To add the property to the fronenduser Model you create your own model Classes/Domain/Model/FronendUser.php:
class Tx_MyExt_Domain_Model_FrontendUser extends Tx_Extbase_Domain_Model_FrontendUser {
/**
* #var string/integer
*/
protected $vipnumber;
}
Add a getter and a setter. Now you create your own FrontendUserRepository and extend the extbase one like you did with the model. You use this repository in your Controller. Now you're almost there: Tell Extbase via typoscript, that your model is using the fe_users table and everything should work:
config.tx_extbase {
persistence{
Tx_MyExt_Domain_Model_FrontendUser{
mapping {
tableName = fe_users
}
}
}
}
To disable storagePids in your repository in general, you can use this code inside your repository:
/**
* sets query settings repository-wide
*
* #return void
*/
public function initializeObject() {
$querySettings = $this->objectManager->create('Tx_Extbase_Persistence_Typo3QuerySettings');
$querySettings->setRespectStoragePage(FALSE);
$this->setDefaultQuerySettings($querySettings);
}
After this, your Querys will work for all PIDs.
I didn't have the opportunity to work with frontend users yet, so I don't know if the following applies in this case:
In a custom table I stumbled uppon the fact, that extbase repositories automatically have a look at the pids stored in each entry and check it against a set storage pid (possibly also the current pid if not set). Searching for a uid usually means you have a specific dataset in mind so automatic checks for other values could logically be ignored which would support your experiences. I'd try to set the storage pid for your extension to the place the frontend users are stored in ts-setup:
plugin.[replace_with_extkey].persistence.storagePid = [replace_with_pid]
I have a file/class::method (HelperAdmin.php/HelperAdmin::menuItem()) which extracts data from DB to generate main menu and submenu.
I have to get this data after the menu being generated but I don't want to call this method twice.
So I have created a static array in HelperAdmin class. It looks like this:
class HelperAdmin {
static $arrMenuItems;
...
public static function menuItem() {
....get $items....
self::$arrMenuItems = $items;
return $items;
}
....
}
But here is a problem. If I call the METHOD again:
$items=HelperAdmin::menuItem();
...I can get data.
In other hand if I try to get data through a static array:
$items=HelperAdmin::$arrMenuItems;
...it just returns null.
I hope to see some ideas. After all, if your opinion is that using static variable here (from Yii architecture view) is not the best solution, I'd like to get your advice!
#bool.dev:
OK, imagine the following scheme:
1. We have the main file
/modules/admin/views/layouts/admin.php
which is in essence our backend template.
2. We have the helper here:
/modules/admin/components/HelperAdmin.php
which contains:
* a class HelperAdmin,
* menuItem() method and
* a class static array $arrMenuItems.
A content of this ARRAY returned by calling HelperAdmin::menuItem().
We want get data from the HelperAdmin class twice:
1. While generating a menu in admin.php;
2. As part of content which we get with variable $content putting it at this file.
$content in turn is generated in another file:
/modules/admin/views/generator/index.php
So as you can see, our page compounds itself from the template file /modules/admin/views/layouts/admin.php and data ($content) getting from /modules/admin/views/generator/index.php.
First I get data for menu:
HelperAdmin::menuItem();
$items=HelperAdmin::$arrMenuItems;
$this->widget(....
'items'=>$items,
...),
));
That's OK. Notice that after this a static array $arrMenuItems already is generated in HelperAdmin.
Next I'm trying to get the same data ($arrMenuItems generated earlier) in file /modules/admin/views/generator/index.php to place it as $content:
$items=HelperAdmin::$arrMenuItems
And here I can't get it as described above.
Well, I hope it made a situation more clear (?).
I'm learning Kohana at the mo and encountering the following error when trying to extend a model to use ORM.
Declaration of Model_Message::create() should be compatible with that of Kohana_ORM::create()
I've enabled the orm in my bootstrap along with the database. The error highlights the following line on the error dump.
class Model_Message extends ORM {
And here is the model code I'm using and failing with...
<?php defined('SYSPATH') or die('No direct script access');
/**
* Message modal
* Handles CRUD for user messages
*/
class Model_Message extends ORM {
/**
* Adds a new message for a user
*
* #param int user_id
* #param string user's message
* #return self
*/
public function create($user_id, $content)
{
$this->clear();
$this->user_id = $user_id;
$this->content = $content;
$this->date_published = time();
return $this->save();
}
}
I've been going through the api documentation and everything is saying that this way of implementing the orm from the model is the correct way to do so. Any pointers would be great.
You need to rename your method (create_message for example) or make it compatible with ORM (because it has the it's own create method which you try to override).