Symfony3 Profiler Storage - redis

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

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?

Laravel change Connection for Notifications table

I am implementing Laravel 5.3 Notifications at the moment which is working very nice.
At the moment I am using 'email' as a notifications channel but I want to add 'database' too. I am using different databases/connections for languages and want to store the notifications in a central database / Connection.
How do I use a different database connection for notifications?
I already tried creating a Notifications model but that did not work:
namespace App;
use Illuminate\Database\Eloquent\Model;
class Notifications extends Model
{
protected $connection = 'system';
}
Hackish solution. But tried and tested on a MongoDB connection.
What needs to be modified;
The Notifiable trait
The DatabaseNotification model
Optionally (nothing changes if you are using mysql) modify the HasNotifications trait
Modify the DatabaseNotificationCollection.Again this is useful for a non-mysql connection
Step One : Create a custom Notifiable Trait
Copy the contents from Illuminate\Notifications\Notifiable and create a new file in your custom path...say App\Overrides\Notifications\Notifiable.
Your file will feature two changes...the namespace and you have to load the RoutesNotifications trait since we are not copying it over.
<?php
namespace App\Overrides\Notifications;
use use Illuminate\Notifications\RoutesNotifications;
trait Notifiable{
//The rest of the code remains
}
Step Two : Create a custom DatabaseNotification model
Follow the same procedure as above and copy the contents of the Illuminate\Notifications\DatabaseNotification file to the custom path that we created above...App\Overrides\Notification\DatabaseNotification
This is a standard Eloquent model and the connection change actually happens here
<?php
namespace App\Overrides\Notification;
//Use this if on mongodb.otherwise use to Illuminate\Database\Eloquent\Model
use Jenssegers\Mongodb\Eloquent\Model;
use Illuminate\Notifications\DatabaseNotificationCollection;
class DatabaseNotification extends Model
{
protected $connection = 'YOUR_CONNECTION_NAME_GOES HERE';
}
As of this point this should work if you are on a mysql connection.
To try this out change the Notifiable trait on the user model to use App\Overrides\Notifications\Notifiable. The notifications will use the connection you specified.
Users of MongoDB will have to take extra steps since the most popular driver I know of does not yet support MorphMany relations which are put to use for Laravel notifications.
Since that is not the asked question we leave it at that :-)
On Laravel 5.7 based on #Bernard answer
User.php
<?php
namespace App;
// implement the override Notifiable trait
use App\Traits\Override\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use Notifiable;
}
Notifiable.php
<?php
namespace App\Traits\Override;
use Illuminate\Notifications\RoutesNotifications;
trait Notifiable
{
use HasDatabaseNotifications, RoutesNotifications;
}
HasDatabaseNotifications.php
<?php
namespace App\Traits\Override;
use App\Models\Override\MultiConnectionDatabaseNotification;
trait HasDatabaseNotifications
{
/**
* Get the entity's notifications.
*
* #return \Illuminate\Database\Eloquent\Relations\MorphMany
*/
public function notifications()
{
return $this->morphMany(MultiConnectionDatabaseNotification::class, 'notifiable')->orderBy('created_at', 'desc');
}
/**
* Get the entity's read notifications.
*
* #return \Illuminate\Database\Query\Builder
*/
public function readNotifications()
{
return $this->notifications()->whereNotNull('read_at');
}
/**
* Get the entity's unread notifications.
*
* #return \Illuminate\Database\Query\Builder
*/
public function unreadNotifications()
{
return $this->notifications()->whereNull('read_at');
}
}
MultiConnectionDatabaseNotification.php
<?php
namespace App\Models\Override;
use Illuminate\Notifications\DatabaseNotification as DatabaseNotification;
class MultiConnectionDatabaseNotification extends DatabaseNotification
{
// set your preferred connection here
protected $connection = 'oracle';
}
It's pretty simple, Just add protected $connection = 'YOUR CONNECTION NAME'; at Illuminate\Notifications\DatabaseNotification
That's all and it will work :)
You don't need to create new models if you are going to use one notification table with same connection.
My code will works if ur using different connection for USER model.

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

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]

The class 'Doctrine\ORM\PersistentCollection' was not found in the chain configured namespaces

I am using in project Zend Framework 2 with Doctrine 2 ORM.
I am trying to persist a many to many relation. I followed th documentation described here (manytomany).
While trying to persist data: $em->persist($form->getData()); I got the error:
"The class 'Doctrine\ORM\PersistentCollection' was not found in the chain configured namespaces".
Any suggestions ?
To be more clear I added below some code:
First I annotated entities like documentation said, for many to many relation:
/**
* #ORM\ManyToMany(targetEntity="\User\Entity\Client", mappedBy="reportSettings")
*/
private $client;
public function __construct() {
$this->client = new ArrayCollection();
}
and
/**
* #ORM\ManyToMany(targetEntity="\Statistics\Entity\ReportSettings", inversedBy="client")
* #ORM\JoinTable(name="report_client_settings",
* joinColumns={#ORM\JoinColumn(name="client_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="report_setting_id", referencedColumnName="id")})
*/
private $reportSettings;
public function __construct() {
$this->reportSettings = new ArrayCollection();
}
And in the controller
$form = new UpdateReportSettingsForm();
$form->bind($reportSettings);
$request = new Request();
if ($request->isPost()) {
$form->setData($request->getPost());
if ($form->isValid()) {
$data = $form->getData();
$em->persist($data); // here I got the error - The class 'Doctrine\ORM\PersistentCollection' was not found in the chain configured namespaces
$em->flush();
}
I also use in the form DoctrineModule\Form\Element\ObjectMultiCheckbox.
An simple var_dump($data) - return a persistent collection.
The error appear because the form was not defined correctly. The right way how to do a many to many relation i founded here - http://laundry.unixslayer.pl/2013/zf2-quest-zendform-many-to-many/
Have you added this piece of code in the beginning of your entity where you are mapping
use Doctrine\Common\Collections\ArrayCollection;

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