Same paginator for every controller in ZF2 - oop

I'm writing my first app in ZF2, and I want to create pagination system.
Currently, I have something like this in my controllers:
$pagLimit = $this->params()->fromQuery('limit', 1000);
$pagPage = $this->params()->fromQuery('page', 1);
$orderDir = $this->params()->fromQuery('dir', 'ASC');
$orderBy = $this->params()->fromQuery('column', 'id');
$result = $this->getMapper()->getList($orderDir, $orderBy);
$paginator = new Paginator(new ArrayAdapter($result));
$paginator->setItemCountPerPage($pagLimit);
$paginator->setCurrentPageNumber($pagPage);
I think that my solution is not quite good..
If I want to change e.g. default limit of items per page, I have to modify all my controllers. Also, I have to remember to send two arguments for all mapper methods which are getting lists of data.
My first thought was to use inheritance ("MyController" with methods like: setPaginationParams(), and setPaginator($data)).
Then I would have to remember to invoke "my controller" methods in every controller.
But maybe there is a better way to implement the same paginator for every controller in my module? MVC event? Create custom class and use DI?
What is the best way to implement this functionality?
Could you just give me some hints?
I'm new to ZF2 and OOP concepts. :(

You could always extend the paginator with your own, and have default values set.
You could even just pass in the request object or params object and then let the Paginator internally handle some things for you to save setting up default values etc.

Related

Remove all existing objects from Aurelia Validate

Without going into depth, I need to remove all validation objects from a Validation Controller without having access to those objects outside of the controller. Basically this is to cleanup existing validation objects before adding new ones. It's complex.
Very simply, I need to do this:
this.validationCtrl.removeAllObjects();
One possibility I've attempted is to inspect the validation controller and iterate through its objects property, but I'm unable to correctly access these or give the .removeObject() what it needs.
const validationObjects = this.validationCtrl.objects;
validationObjects.forEach(obj => this.validationCtrl.removeObject(obj));
This also doesn't work.
What is the best way to blindly remove all existing validation controller objects?
The following code works to delete all existing objects from the current controller:
const validationEntries = Array.from(this.validationCtrl.objects);
validationEntries.forEach(([key]) => {
this.validationCtrl.removeObject(key);
});

How can I access query string parameters for requests I've manually dispatched in Laravel 4?

I'm writing a simple API, and building a simple web application on top of this API.
Because I want to "consume my own API" directly, I first Googled and found this answer on StackOverflow which answers my initial question perfectly: Consuming my own Laravel API
Now, this works great, I'm able to access my API by doing something like:
$request = Request::create('/api/cars/'.$id, 'GET');
$instance = json_decode(Route::dispatch($request)->getContent());
This is great! But, my API also allows you to add an optional fields parameter to the GET query string to specify specific attributes that should be returned, such as this:
http://cars.com/api/cars/1?fields=id,color
Now the way I actually handle this in the API is something along the lines of this:
public function show(Car $car)
{
if(Input::has('fields'))
{
//Here I do some logic and basically return only fields requested
....
...
}
I would assume that I could do something similar as I did with the query string parameter-less approach before, something like this:
$request = Request::create('/api/cars/' . $id . '?fields=id,color', 'GET');
$instance = json_decode(Route::dispatch($request)->getContent());
BUT, it doesn't seem so. Long story short, after stepping through the code it seems that the Request object is correctly created (and it correctly pulls out the fields parameter and assigns id,color to it), and the Route seems to be dispatched OK, but within my API controller itself I do not know how to access the field parameter. Using Input::get('fields') (which is what I use for "normal" requests) returns nothing, and I'm fairly certain that's because the static Input is referencing or scoping to the initial request the came in, NOT the new request I dispatched "manually" from within the app itself.
So, my question is really how should I be doing this? Am I doing something wrong? Ideally I'd like to avoid doing anything ugly or special in my API controller, I'd like to be able to use Input::get for the internally dispatched requests and not have to make a second check , etc.
You are correct in that using Input is actually referencing the current request and not your newly created request. Your input will be available on the request instance itself that you instantiate with Request::create().
If you were using (as you should be) Illuminate\Http\Request to instantiate your request then you can use $request->input('key') or $request->query('key') to get parameters from the query string.
Now, the problem here is that you might not have your Illuminate\Http\Request instance available to you in the route. A solution here (so that you can continue using the Input facade) is to physically replace the input on the current request, then switch it back.
// Store the original input of the request and then replace the input with your request instances input.
$originalInput = Request::input();
Request::replace($request->input());
// Dispatch your request instance with the router.
$response = Route::dispatch($request);
// Replace the input again with the original request input.
Request::replace($originalInput);
This should work (in theory) and you should still be able to use your original request input before and after your internal API request is made.
I was also just facing this issue and thanks to Jason's great answers I was able to make it work.
Just wanted to add that I found out that the Route also needs to be replaced. Otherwise Route::currentRouteName() will return the dispatched route later in the script.
More details to this can be found on my blog post.
I also did some tests for the stacking issue and called internal API methods repeatedly from within each other with this approach. It worked out just fine! All requests and routes have been set correctly.
If you want to invoke an internal API and pass parameters via an array (instead of query string), you can do like this:
$request = Request::create("/api/cars", "GET", array(
"id" => $id,
"fields" => array("id","color")
));
$originalInput = Request::input();//backup original input
Request::replace($request->input());
$car = json_decode(Route::dispatch($request)->getContent());//invoke API
Request::replace($originalInput);//restore orginal input
Ref: Laravel : calling your own API

Yii global variables and setting issue

I am developing a social networking website using Yii. While frequently using the following things I am having great data manageability issue.
- User ID
- Current user ID (the user which profile is the owner viewing)
- Is owner???
where can I define these things.
I would something like
if(Yii::app()->owner==ME){
//do something
}
// and similarly
if($this->isMyFreind(<Current user ID>){
}
// $this(CanIView()){
}
I want these functions to be public for any page? But how?
In other words
Where can I put my library which contains my own favorite functions like text shortening, image cropping, date time format etc etc??
In Yii, you can do achieve this by making a class (under protected/compoents) which inherits
CApplicationComponent
class. And then you call any property of this class globally as a component.
class GlobalDef extends CApplicationComponent {
public $aglobalvar;
}
Define this class in main config under components as:
'globaldef' => array('class' => 'application.components.GlobalDef '),
And you can call like this:
echo Yii::app()->globaldef->aglobalvar;
Hope that can help.
According to the MVC model, things like image cropping or date time formats would go in models. You would simply create models for that.
I usually use a globals.php file with all my common functions
In the index.php (yip the one in the root):
$globals='protected/globals.php';
require_once($globals);
I can then call my global functions anywhere. For example, I shorten certain Yii functions like:
function bu($url=null){
static $baseUrl;
if ($baseUrl===null)
$baseUrl=Yii::app()->request->baseUrl;
return $url===null ? $baseUrl : $baseUrl.'/'.ltrim($url,'/');
}
So I can then call the Yii::app()->request->baseUrl by simply calling bu()

Sending more data to changeIdentity in CWebUser by overriding it?

Disclaimer: Complete beginner in Yii, Some experience in php.
In Yii, Is it OK to override the login method of CWebUser?
The reason i want to do this is because the comments in the source code stated that the changeIdentity method can be overridden by child classes but because i want to send more parameters to this method i was thinking of overriding the login method too (of CWebUser).
Also if that isn't such a good idea how do you send the extra parameters into the changeIdentity method.(By retrieving it from the $states argument somehow ??). The extra parameters are newly defined properties of UserIdentity class.
It is better to first try to do what you wish to do by overriding components/UserIdentity.php's authenticate method. In fact, that is necessary to implement any security system more advanced than the default demo and admin logins it starts you with.
In that method, you can use
$this->setState('myVar', 5);
and then access that anywhere in the web app like so:
Yii::app()->user->getState('myVar');
If myVar is not defined, that method will return null by default. Otherwise it will return whatever it was stored as, in my example, 5. These values are stored in the $_SESSION variable, so they persist as long as the session does.
UPDATE: Okay, took the time to learn how this whole mess works in Yii for another answer, so I'm sharing my findings here as well. The below is mostly copy pasted from a similar answer I just gave elsewhere. This is tested as working and persisting from page to page on my system.
You need to extend the CWebUser class to achieve the results you want.
class WebUser extends CWebUser{
protected $_myVar = 'myvar_default';
public function getMyVar(){
$myVar = Yii::app()->user->getState('myVar');
return (null!==$myVar)?$myVar:$this->_myVar;
}
public function setMyVar($value){
Yii::app()->user->setState('myVar', $value);
}
}
You can then assign and recall the myVar attribute by using Yii::app()->user->myVar.
Place the above class in components/WebUser.php, or anywhere that it will be loaded or autoloaded.
Change your config file to use your new WebUser class and you should be all set.
'components'=>
'user'=>array(
'class'=>'WebUser',
),
...
),

How can I use Zend Layout with Zend View if I call the view from a model?

Basically, I want to render a view and layout from a model. Don't ask me why.
First of all, the views work as intended and I'm loading them into a variable for my perverse use later on. I am also fully aware that I could always do partial scripts. It seems to be a valid fallback, but it just doesn't cut it.
What I want to do is to get the layout to work automatically just like in the case with controllers and views.
Right now I employ something like this:
// Class blablabla
$layout = new Zend_Layout();
$layout->enableLayout();
$layout->setView($view);
// Ugly url, I know, I'm experimenting and they work
$body = $layout->render('mailer/layout/mail');
$body .= $view->render('mailer/templates/' . $type . '.phtml');
The problem is that $body contains the layout and only then the actual view. Any advice? What am I doing wrong?
Assuming that your layout contains the default $this->layout()->content somewhere, you'd want this:
$layout->content = $view->render('...');
$body = $layout->render('...');
Source: http://www.wowww.ch/2009/03/16/zend-mail-avec-template-standardise-avec-zend-layout-et-zend-view/
I think my first note would have to be that you're trying to use a hammer as a screwdriver. As I'm sure you know, in the MVC model the view is the rendering, and is logically distinct (separate) from the model. I'm not sure you're going to find a happy solution to this, since you're crossing the streams.