Tx_Extbase_Domain_Repository_FrontendUserRepository->findAll() not working in typo3 4.5.30? - repository

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]

Related

Typo3 Repository update

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?

Yii2 Event not run after login

I created a Behavior which contains function. This function should be afterLogon of User (yii/web/User::EVENT_AFTER_LOGIN).
But this function never will be triggered unfortunatelly.
I have a Behaviour class for the user model:
class UserBehavior extends Behavior
{
/**
* #inheritdoc
* #param \yii\base\Component $owner
*/
public function attach($owner)
{
parent::attach($owner);
$owner->on(\yii\web\User::EVENT_AFTER_LOGIN, [$this, 'updateLoginInformation']);
}
/**
* Update login information data:
* - login ip address
* - login time
*/
public function updateLoginInformation()
{
/** #var \common\models\User $owner */
$owner = $this->owner;
$owner->logged_in_ip = Yii::$app->request->getUserIP();
$owner->logged_in_at = time();
$owner->save();
}
}
I declared the events and the attach too.
But this events never be run after login...
I attached this behavior to the user model:
/**
* #inheritdoc
*/
public function behaviors()
{
return [
TimestampBehavior::className(),
UserBehavior::className()
];
}
If I know well the the EVENT_AFTER_LOGIN will be triggered automatically by the Yii framework, this is the reason why I do not trigger it again.
And I do not where is the problem, because the updageLoginInformatin never called.
I usually use any logic I want in a model inside the proper action that calls it (IE: actionLogin). But I like your approach.
I just made a test here and the correct way to call the event is something like this:
$user = \Yii::$app->user;
$user->on($user::EVENT_AFTER_LOGIN, [$this, 'updateLoginInformation']);
I didn't create a behavior class, I just added this lines in my init(), but the logic is probably the same as yours.

Symfony3 Profiler Storage

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

Kohana ORM module not working

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).

Doctrine2 ArrayCollection

Ok, I have a User entity as follows
<?php
class User
{
/**
* #var integer
* #Id
* #Column(type="integer")
* #GeneratedValue
*/
protected $id;
/**
* #var \Application\Entity\Url[]
* #OneToMany(targetEntity="Url", mappedBy="user", cascade={"persist", "remove"})
*/
protected $urls;
public function __construct()
{
$this->urls = new \Doctrine\Common\Collections\ArrayCollection();
}
public function addUrl($url)
{
// This is where I have a problem
}
}
Now, what I want to do is check if the User has already the $url in the $urls ArrayCollection before persisting the $url.
Now some of the examples I found says we should do something like
if (!$this->getUrls()->contains($url)) {
// add url
}
but this doesn't work as this compares the element values. As the $url doesn't have id value yet, this will always fail and $url will be dublicated.
So I'd really appreciate if someone could explain how I can add an element to the ArrayCollection without persisting it and avoiding the duplication?
Edit
I have managed to achive this via
$p = function ($key, $element) use ($url)
{
if ($element->getUrlHash() == $url->getUrlHash()) {
return true;
} else {
return false;
}
};
But doesn't this still load all urls and then performs the check? I don't think this is efficient as there might be thousands of urls per user.
This is not yet possible in a "domain driven" way, ie. just using objects. You should execute a query to check for the existance:
SELECT count(u.id) FROM User u WHERE ?1 IN u.urls AND u.id = ?2
With Doctrine 2.1 this will be possible using a combination of two new features:
Extra Lazy Collections
#IndexBy for collections, so you would define #OneToMany(targetEntity="Url", indexBy="location")
ExtraLazy Collection Support for index by using ->contains().
Points 1 and 2 are already implemented in Doctrine 2 master, but 3 is still missing.
You should try using the exists method on the collection and manually compare values.