Drupal: Getting user name on user account page without breaking performance - performance

I have multiple blocks shown on the user profile page, user/uid
On each of them, I need to print the user name.
I've been doing a $user = user_load(arg(1)); print $user->name; on each block. Since there is no caching, as you can image the performance is HORRIBLE.
Is there either a way to get the user name more efficiently or to cache user_load.
Thanks.

Just add an intermediate function to provide the static caching yourself:
/**
* Proxy for user_load(), providing static caching
* NOTE: Only works for the common use of user_load($uid) - will NOT load by name or email
*
* #param int $uid - The uid of the user to load
* #param bool $reset - Wether to reset the static cache for the given uid, defaults to FALSE
* #return stdClass - A fully-loaded $user object upon successful user load or FALSE if user cannot be loaded.
*/
function yourModule_user_load_cached($uid, $reset = FALSE) {
static $users = array();
// Do we need to (re)load the user?
if (!isset($users[$uid]) || $reset) {
$users[$uid] = user_load($uid);
}
return $users[$uid];
}

Use menu_get_object() which is the proper way to retrieve an object (user, node, etc.) loaded from the URL of a properly declared page. It will return the user object that has already been loaded using the uid found at arg(1) for a menu item which use %user in its path (ie. $items['user/%user'], $items['user/%user/view'], etc. in user_menu().
$account = menu_get_object('user');

The user is a global.
function myfunction() {
global $user;
}

Related

Drupal 8 - Add custom cache context

I have the following situation: I want to hide or show some local Tasks (Tabs) based on a field on the current user. Therefore I have implemented a hook_menu_local_tasks_alter() in my_module/my_module.module:
function my_module_menu_local_tasks_alter(&$data, $route_name, \Drupal\Core\Cache\RefinableCacheableDependencyInterface &$cacheability) {
... some logic ...
if ($user->get('field_my_field')->getValue() === 'some value')
unset($data['tabs'][0]['unwanted_tab_0']);
unset($data['tabs'][0]['unwanted_tab_1']);
... some logic ...
}
This works fine but I need to clear the caches if the value of field_my_field changes.
So I found that I need to implement a Cache Context like this in my my_module_menu_local_tasks_alter:
$cacheability
->addCacheTags([
'user.available_regions',
]);
I have defined my Cache Context like this:
my_module/my_module.services.yml:
services:
cache_context.user.available_regions:
class: Drupal\my_module\CacheContext\AvailableRegions
arguments: ['#current_user']
tags:
- { name: cache.context }
my_module/src/CacheCotext/AvailableRegions.php:
<?php
namespace Drupal\content_sharing\CacheContext;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Cache\Context\CacheContextInterface;
use Drupal\Core\Session\AccountProxyInterface;
/**
* Class AvailableRegions.
*/
class AvailableRegions implements CacheContextInterface {
protected $currentUser;
/**
* Constructs a new DefaultCacheContext object.
*/
public function __construct(AccountProxyInterface $current_user) {
$this->currentUser = $current_user;
}
/**
* {#inheritdoc}
*/
public static function getLabel() {
return t('Available sub pages.');
}
/**
* {#inheritdoc}
*/
public function getContext() {
// Actual logic of context variation will lie here.
$field_published_sites = $this->get('field_published_sites')->getValue();
$sites = [];
foreach ($field_published_sites as $site) {
$sites[] = $site['target_id'];
}
return implode('|', $sites);
}
/**
* {#inheritdoc}
*/
public function getCacheableMetadata() {
return new CacheableMetadata();
}
}
But every time I change the value of my field field_my_field I still need to clear the caches, so the Context is not working. Could anybody point me in the right direction how to get this solved or how to debug such kind of thigs?
Instead of providing a custom cache context, you should be able to use the default cacheability provided by core. I believe the issue is not so much the creation of the cacheable metadata, its seems that your hook_menu_local_tasks_alter is altering content that doesn't know it now relies on the user. So I believe you need 2 things:
General cache contexts that says 'this menu content now relies on the user', eg. user cache context.
Additional use of cache tag for a specific user to say: 'once this is cached, when this user entity changes, go regenerate the local tasks for this user'.
Note that HOOK_menu_local_tasks_alter provides a helper for cacheability, the third param of $cacheability. Drupal core also provides a mechanism here that allows us to say 'this piece of cache data relies on this other piece of cache data'.
Thus you should be able to do something like:
function my_module_menu_local_tasks_alter(&$data, $route_name, RefinableCacheableDependencyInterface &$cacheability) {
... some logic ...
// We are going to alter content by user.
$cacheability->addCacheableDependency($user);
// Note if you still really need your custom context, you could add it.
// Also note that any user.* contexts should already be covered above.
$cacheability->addCacheContexts(['some_custom_contexts']);
if ($user->get('field_my_field')->getValue() === 'some value')
unset($data['tabs'][0]['unwanted_tab_0']);
unset($data['tabs'][0]['unwanted_tab_1']);
... some logic ...
}

Deleting a File from Storage after Download

I'm working on a media asset management system. I want the user to be able to fill out a form with file width, height, extension and colorspace, then transform the image and serve it back as a download.
I can get that to work by responding to the Post-Request with the URL of the newly created file.
What I want is for that file to be deleted after download or after some time.
(Or, preferably, a way to use laravels download() Response, which I apparently can't use inside an Axios/Ajax post request).
Thanks for your help :)
There are two ways you can do this.
Let's assume you have a file in storage/app/download.zip:
1. Because Laravel uses Symfony's HttpFoundation internally, you can use the deleteFileAfterSend method:
public function download()
{
return response()
->download(storage_path('app/download.zip'))
->deleteFileAfterSend(true);
}
2. Create a Terminable Middleware that deletes the file after the download response was prepared.
class StorageDownload
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
return $next($request);
}
public function terminate($request, $response)
{
\Storage::delete('download.zip');
}
}
You'll need to register the middlware and assign it to your route for it to work.
As for triggering the download using JavaScript, something as trivial as setting the window location will work:
axios
.post('files/export/' + file.id, formData)
.then(function() {
window.location = 'files/download/' + file.id
});
Don't worry, this will not navigate away from your current page, it will just trigger the download.
for me , i like to handle this case using cron
you can read details here
add any column to check it's been downloaded or not (e.g status with value 0 or 1)
and you can create accessor on your model to count the date diff
public function getRangeDateAttribute(){
$today = Carbon::now()->startOfDay();
$downloadDate = $this->created_at;
$datediff = $today->diffInDays($downloadDate);
return $datediff
}
then sample code inside task scheduling :
if($media->status == 1 || $media->rangeDate > 7 ){ //checking the status & range date passed specific days you wanted
do whatever you wanted here
}

typo3 extbase validate for multiple records

I have written one extbase plugin, that creates the FE users from front end form.
The create action is something like this
/**
* action create
*
* #param \TYPO3\Usermanagement\Domain\Model\Users $newUsers
* #return void
*/
public function createAction(\TYPO3\Usermanagement\Domain\Model\Users $newUsers) {
$this->usersRepository->add($newUsers);
}
Here I want to validate for same username or email already exists or not.
How can I do this ?
Any suggestions ?
Thank you.
You don't need to bind a $newUser as an action's param, instead you can just read some fields using $this->request->hasArgument('something') and $this->request->getArgument('something') to validate properties yourself, and create new user object manually like.
public function createAction() {
$newUsers = new \TYPO3\Usermanagement\Domain\Model\Users();
// do something with $newUsers object...
$this->usersRepository->add($newUsers);
}
It will not throw an exception in case when there's no valid user object in the request, so it will allow you to handle form's error as you want/need.
It will also allow you to use some preprocessing before saving ie hashing/salting passwords.

How to get order store config variable in Admin

I created a module where it return via xml the payment details in Magento Admin order page.
It works very well with a single store config data.
But if I have diferents payment credentials for Store Id 1 and store Id 2 [p.e. for backoffice key 1111-1111-1111-1111 (store 1) and other 2222-2222-2222-2222 (store 2), I only can return the default values for admin view with this function...
$subent_id = Mage::getStoreConfig('payment/multibancopayment/subentidade');
Does any one khow how i can get store specific data based in order store id?
Example: in admin order page details, if the order was made in store 1 I need 1111-1111-1111-1111, but if was made in store 2, I need 2222-2222-2222-2222. For now I'm just getting default values with the function above.
Did you try
$subent_id = Mage::getStoreConfig('payment/multibancopayment/subentidade', $storeIdHere);
See /app/Mage.php
/**
* Retrieve config value for store by path
*
* #param string $path
* #param mixed $store
* #return mixed
*/
public static function getStoreConfig($path, $store = null)
{
return self::app()->getStore($store)->getConfig($path);
}
Entire class here

Passing variables before controller URI segment in CodeIgniter

I would like to pass some site-wide validated variables before controller segment in URL.
Example:
Default URL would be:
www.mysite.com/controller/method/variable/
Sometimes I'd like to have also URL like this to refer to user created sub-configuration of this site (theme, menus, ...), so user could share this site URL nicely and others would see the site though his custom configurations.
www.mysite.com/username/controller/method/variable
Here username is custom part of base_url. It should be validated against database and set as session variable to use it later in my controllers and change theme for example. Also all the links on the site would start to use www.mysite.com/username as base_url after website is entered with this username in the URL.
One way to solve this would be routing it like this:
controller/method/variable_name1/variable_value1/user_conf/username
...and the add the implementation to every single controller in my project. But this is not an elegant solution.
Is this what you're after:
$route['(:any)/(:any)'] = '$2/$1';
where all your function definitions have the username as the last parameter:
class Controller{function page(var1, var2, ..., varn, username){}}
Or, if you only want in on one specific page you could do something like this:
$route['(:any)/controller/page/(:any)'] = 'controller/page/$2/$1'; //This will work for the above class.
Or, if you want it for a number of functions in a controller you could do this:
$route['(:any)/controller/([func1|func2|funcn]+)/(:any)'] = 'controller/$2/$3/$1';
After messing with this problem for a day I ended up with adding custom router class to my project. I'm working in CodeIgniter 2.0, so the location of this file should be application/core/MY_Router.php
My code is following:
class MY_Router extends CI_Router {
// --------------------------------------------------------------------
/**
* OVERRIDE
*
* Validates the supplied segments. Attempts to determine the path to
* the controller.
*
* #access private
* #param array
* #return array
*/
function _validate_request($segments)
{
if (count($segments) == 0)
{
return $segments;
}
// Does the requested controller exist in the root folder?
if (file_exists(APPPATH.'controllers/'.$segments[0].EXT))
{
return $segments;
}
$users["username"] = 1;
$users["minu_pood"] = 2;
// $users[...] = ...;
// ...
// Basically here I load all the
// possbile username values from DB, memcache, filesystem, ...
if (isset($users[$segments[0]])) {
// If my segments[0] is in this set
// then do the session actions or add cookie in my cast.
setcookie('username_is', $segments[0], time() + (86400 * 7));
// After that remove this segment so
// rounter could search for controller!
array_shift($segments);
return $segments;
}
// So segments[0] was not a controller and also not a username...
// Nothing else to do at this point but show a 404
show_404($segments[0]);
}
}

Resources