Our Yii Framework application has the following defined as part of the UserProfileImages model:
public function getProfileImages($param, $user_id) {
if(isset($param['select']) && $param['select']=='all'){
$profile_images = UserProfileImages::model()->findAllByAttributes( array( 'user_id'=>$user_id) );
} else {
$profile_images = UserProfileImages::model()->findByAttributes( array( 'user_id'=>$user_id) );
}
return $profile_images;
}
How would I wire up the above snippet to a widget in my view to return all the images for a given user?
Bonus Question: Which image rotator do you suggest to render the above?
In your view file, add something like this, assuming that your controller specified $user_id:
$this->widget('UserProfileImagesWidget', array(
"userProfileImages" => UserProfileImages::getProfileImages(
array("select" => "all"),
$user_id
),
"user_id" => $user_id
));
Depending on your MVC philosophy, you could also retrieve the userProfileImages data in the controller and pass that data to your view.
Define a widget like this:
class UserProfileImagesWidget extends CWidget {
public $user_id;
public $userProfileImages = array();
public function run() {
$this->render("userProfileImage");
}
}
Finally, in the userProfileImages.php view file, you can do something like this:
if(!empty($this->userProfileImages)) {
// Your display magic
// You can access $this->user_id
}
As a side note: You might want to change the order of your parameters in getProfileImages. If $user_id is the first parameter, you can leave out $params completely in case you don't want to specify any.
Related
i have a page in my website (zendframework 1) that parses the GET parameter and query its data from the database to show it to the user.
-> my current url : https://example.com/index.php/product/info?id=123
i want my url to be more human readable
-> https://example.com/index.php/product/iphone-7s-white
so basicaly i want to parse the GET parameter in the url and query the name of the product from the database in order to make it appear as the page name in the url.
i came across some solutions, one of them is achieved by looping through the database (in bootstrap.php) and adding a route for each product, but this seems like a mess, (products can reach 200k or maybe more than that).
is there a better solution for my problem ? thanks in advance
So basically, ZF1 provides a default route that leads to the controller/action of the names from the url.
You can add custom routes from the application/Bootstrap.php file by adding a function there:
/**
* This method loads URL routes defined in /application/configs/routes.ini
* #return Zend_Router
*/
protected function _initRouter() {
$this->bootstrap('frontController');
$front = $this->getResource('frontController');
$router = $front->getRouter();
$router->addRoute(
'user',
new Zend_Controller_Router_Route('product/:slug', array(
'controller' => 'product',
'action' => 'details',
), array(
'slug' => '[A-Za-z0-9-]+',
))
);
return $router;
}
And here you go!
As described by Chris, you need to change your controller code to handle the request. Another solution would be to use an extra action.
final class ProductController
{
public function infoAction()
{
$product = $table->find($this->_param('id'));
$this->view->product = $product;
}
public function detailsAction()
{
$product = $table->fetch(['slug' => $this->_param('slug')]);
$this->view->product = $product;
$this->render('info');
}
}
Now, assuming you do a lot of processing in infoAction, you could go with a forward:
final class ProductController
{
public function infoAction()
{
$product = $table->find($this->_param('id'));
$this->view->product = $product;
}
public function detailsAction()
{
$product = $table->fetch(['slug' => $this->_param('slug')]);
$this->forward('info', 'product', [
'id' => $product->id,
]);
}
}
It is less efficient (2 requests instead of one), but allows you to reuse your code.
I have a couple of different 'items' on my website that I am building with cakePHP, for instance a Recipe and a ShoppingList.
I want certain items in my view (e.g. update and delete functionality links) to only be visible to the person who uploaded that item.
I want to add a function that would compare any given id to the currently logged in user's id. It would look something like this:
public function compareUser($id){
if(!empty($this->userInfo) && $this->userInfo['User']['id'] == $id){
return true;
}
}
$this->userInfo is set in beforeFilter:
$this->userInfo = $this->User->find('first', array('conditions' => array('id' => $this->Auth->user('id'))));
I have tried putting it in my appController, but that doesn't seem to work.
How can I implement this properly? Thanks!
This is best done using the isAuthorized($user) method.
All the information about your current user is stored in $this->Session->read('Auth.User') (this retrieves the full array, if you just wanted to get their 'id' you use $this->Auth->user('id') as you already did).
From the above it should hopefully be clear that normally you don't need to retrieve the user's details through an extra query as they are already stored in the Auth component of the session :)
Make sure in the setup for your Auth component you have 'authorize' => 'controller' and add the following to your AppController:
public function isAuthorized($user) {
//I want the default to be allow the user access so I will return true
return TRUE;
}
Then add the following to your RecipesController (and ShoppingListsController if you want the same thing there):
public function isAuthorized($user) {
if ($this->action === 'update' || $this->action === 'delete') {
$recipe = $this->Recipe->find(
'first',
'conditions' => array(
'id' => $this->params['pass'][0]
)
'fields' => array(
'user_id'
)
);
if ($this->Auth->user('id') == $recipe['Recipe']['user_id']) {
return TRUE;
}
else {
return FALSE;
}
}
return parent::isAuthorized($user);
}
Now if someone tries to access www.yourDomain.com/recipes/update/2 or www.yourDomain.com/recipes/delete/2 it will check if the current user's id is 2, if it is you're good to go, if not then it blocks them from that page.
Edit:
Easiest way to have a method accessible from all places I would suggest putting it in the AppModel that way all your models will inherit it:
//inside AppModel
public function isOwnedBy($id) {
if (AuthComponent::user('id) == $id) {
return TRUE;
}
return FALSE;
}
In my project we display custom widgets on our customers pages. The widgets themselves do not change very often, so I feel view caching could be extremely useful here.
But every widget is different based on which company in our system is requesting it.
My question is using the cache helper...or any other method, can I cache the widget based on the company id?
<?php
App::uses('AppController', 'Controller');
class widgetController extends AppController {
public $helpers = array( 'Cache' );
public $cacheAction = array(
'iframeForm' => 3600,
);
public $uses = array('Company');
public function index( $company_id ) {
//... Load up a ton of data
$this->layout = 'widget';
$this->set( compact(/* Set a ton of data */) );
}
}
Is it possible to cache the index view based on the company id so that:
/widget/index/1
is served one copy from cache, but:
/widget/index/2
will get a different copy from the cache?
We are currently running on cake 2.3 and php5.3 we have plans to move to cake2.4 and php 5.5 if that would offer us any help.
I would do something like this:
Controller:
public function index( $company_id ) {
//... Load up a ton of data
$this->Model->getStuff($company_id);
$this->layout = 'widget';
$this->set( compact(/* Set a ton of data */) );
}
In model:
public function getStuff( $company_id ) {
if(($modelData = Cache::read('modelDataCompanyID_'. $company_id)) == null)
{
$modelData = $this->find('all',array('conditions' =>
array('Model.company_id' => $company_id)));
Cache::write('modelDataCompanyID_'. $company_id, $modelData);
}
return $modeData;
}
}
Is this what you want?
I'm setting up pagination to display a list of images belonging to the user in their account. This is what I have in my controller:
class UsersController extends AppController {
public $paginate = array(
'limit' => 5,
'order' => array(
'Image.uploaded' => 'DESC'
)
);
// ...
public function images() {
$this->set('title_for_layout', 'Your images');
$albums = $this->Album->find('all', array(
'conditions' => array('Album.user_id' => $this->Auth->user('id'))
));
$this->set('albums', $albums);
// Grab the users images
$options['userID'] = $this->Auth->user('id');
$images = $this->paginate('Image');
$this->set('images', $images);
}
// ...
}
It works, but before I implemented this pagination I had a custom method in my Image model to grab the users images. Here it is:
public function getImages($options) {
$params = array('conditions' => array());
// Specific user
if (!empty($options['userID'])) {
array_push($params['conditions'], array('Image.user_id' => $options['userID']));
}
// Specific album
if (!empty($options['albumHash'])) {
array_push($params['conditions'], array('Album.hash' => $options['albumHash']));
}
// Order of images
$params['order'] = 'Image.uploaded DESC';
if (!empty($options['order'])) {
$params['order'] = $options['order'];
}
return $this->find('all', $params);
}
Is there a way I can use this getImages() method instead of the default paginate()? The closest thing I can find in the documentation is "Custom Query Pagination" but I don't want to write my own queries, I just want to use the getImages() method. Hopefully I can do that.
Cheers.
Yes.
//controller
$opts['userID'] = $this->Auth->user('id');
$opts['paginate'] = true;
$paginateOpts = $this->Image->getImages($opts);
$this->paginate = $paginateOpts;
$images = $this->paginate('Image');
//model
if(!empty($opts['paginate'])) {
return $params;
} else {
return $this->find('all', $params);
}
Explanation:
Basically, you just add another parameter (I usually just call it "paginate"), and if it's true in the model, instead of passing back the results of the find, you pass back your dynamically created parameters - which you then use to do the paginate in the controller.
This lets you continue to keep all your model/database logic within the model, and just utilize the controller to do the pagination after the model builds all the complicated parameters based on the options you send it.
Given an html/javascript 'widget' which needs to have certain fields customized before use. For example, the css class ids need to be unique as the widget may appear more than once on the same page.
Let's say I want to keep the markup (js/html) of the widget stored as a template so that I can fill in the values that need to be customized during resuse.
I know that Zend Framework's views give you at least part of this functionality, but each view is generally associated with a particular controller. Given that this widget could be created from any controller, yes still needs to be able to access some properties stored in a controller (or model). Where should I put the widget markup and how then do I fill in the custom values?
Can I create a custom view that can be reused within the same page (appear more than once) as well as on other pages? If so, how do I set that up?
Sounds like you need a ViewHelper http://framework.zend.com/manual/en/zend.view.helpers.html. Create a custom helper that will fetch the data from a model and just simply output it. This way it won't depend on any controller, can be called in either the layout or in any view script. Example:
// views/helpers/Widget.php
class Zend_View_Helper_Widget extends Zend_View_Helper_Abstract
{
protected $_model = null;
protected $_view = null;
public function widget()
{
$data = $this->_getDataFromModel();
return $this->_view->partial('widget.phtml', array('data' => $data));
}
public function setView(Zend_View_Interface $view)
{
if($this->_view === null) {
$this->_view = $view;
}
return $this->_view;
}
protected function _getDataFromModel()
{
$this->_model = $this->_getModel();
return $this->_model->getDataForWidget();
}
protected function _getModel()
{
if($this->_model === null) {
$this->_model = new Model_Widget(); // or whatever it's called
}
return $this->_model;
}
The partial script:
// views/scripts/widget.phtml
<div class="widget-class"><?php echo $this->data; ?></div>
And when you need it in your views just call it like <?php echo $this->widget(); ?>
Note that I'm rendering the widget in a separate partial view script, just to avoid having html/css in the helper itself.
Hope this helps to get you started :)
Zend_View_Helper_Partial
Example:
<?php echo $this->partial('partial.phtml', array(
'css_id' => 'foobar')); ?>
To run this from any other module:
<?php echo $this->partial('partial.phtml', 'partials_module', array(
'css_id' => 'foobar')); ?>
In your partial view script (partial.html) you would then have access to $this->css_id.