yii search form via post instead of get - yii

I have a simple search
public function search() {
$criteria=new CDbCriteria;
$criteria->with = array('agent');
$criteria->compare('full_name',$this->full_name,true);
if ($this->gender_id != "") {
$criteria->compare('gender_id',$this->gender_id);
}
return new CActiveDataProvider($this, array(
'criteria'=>$criteria,
'pagination'=>array(
'pageSize'=>30,
),
));
}
But I don't like that the search parameters appear in the address bar when you use the get method to search. I've changed my search widget to use the post method instead:
$form=$this->beginWidget('CActiveForm', array(
'action'=>Yii::app()->createUrl($this->route),
'method'=>'post',
));
But now when I hit the search button the page just refreshes instead of showing the search results, I assume I'm missing something here...

In your actionAdmin function of the controller replace $_GET by $_POST...
if(isset($_GET['Model']))
$model->attributes=$_GET['Model'];
replace $_GET in above lines by $_POST like:
if(isset($_POST['LoginLog']))
$model->attributes=$_POST['LoginLog'];
On a side note on search it is always advised to use GET instead of POST, the basic rule i use is whenever some data needs to be submitted it should be POST, whenever some data needs to be fetched it should be GET..
Update:
The main reasons I can think of i would use GET for search
1) In searches user needs the functionality to back to previous filter, which if used as get url params, is straight forward.
2) If the filter params are in url, its extremely easy to share results after certain filters..Imagine you want to share some results with a friend, would you give him instructions to filter step by step (In case of POST), or give a direct url(GET)
3) Its very easy to change params from url, imagine currently you are visiting 2nd page, but on page while displaying filters only links to next 5 pages are displayed, but you want to jump to straight 15th page results..
There will be many more advantages, I can think of these at the moment..

Related

How do I pull in page's "url path" field, via php, into the theme?

I'm trying to apply the page's url as a <div> id to be able to target some css on each individual page. After some characters cleanup I'm hoping to get <div id="test-page">.
I have tried pulling it in from the object that I get via
ipContent()->getBreadcrumb()
Unfortunately they are all (including one I need) protected and cannot be echoed out.
[urlPath:protected] => test-page/
Is there a function that I've missed and can use to pull that in? Or a proper method of getting it from the object? Cheers.
And of course, as soon as I posted a question I've found the answer:
I could not get the protected value from [urlPath:protected] => test-page/ when doing this:
$a = ipContent()->getBreadcrumb();
$a = $a[0];
$a = $a->urlPath;
Solution: The way you can pull this is is by replacing $a->urlPath with $a->getUrlPath().
It will work for all the elements in object. Like: $a->updatedAt; needs to be $a->getUpdatedAt();

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

Implementing Google Custom Search with filtering

I have to implement a page with multiple google-powered search forms. We have a license from Google for CSE, and this is the situation:
I have a search form that's present at the top of every page that performs a simple search and displays the results in a separate page. This works.
I have a particular page that, in addition, shows another two search forms: one should filter articles by category, another should filter articles by category and restrict the result to a certain month. I have added a meta key with the publication date to each article for this.
I have gotten a bit lost in the documentation, though: if I add
<gcse:searchbox-only resultsUrl="/[site]/stat/search/google_search_results.html"></gcse:searchbox-only></div>
to the page, I can't filter the results. If I start to meddle with a CustomSearchObject, I don't see an option to show results on a different page.
For category-based filtering, I've tried appending
more:pagemap:metatags-taxonomies:news
to the query argument in the results page URL, and it does work, but I don't understand how to inject this to the form.
For restricting based on dates, I tried adding
&sort=more:pagemap:metatags-pubdate:r:YYYYMMDD:YYYYMMDD
but haven't been able to make it work. Getting the XML does work:
http://www.google.com/search?q=intitle:[mysite]%20more:pagemap:metatags-taxonomies:News&sort=metatags-pubdate:r:20120401:20120830&cx=[mykey]client=google-csbe&output=xml
returns correct results.
Is there documentation that doesn't assume so much? All I find are code snippets without context. I've checked Filtering and sorting, Custom Search Element Control API, and of course this site, but I can't put all the pieces together.
I managed to implement what I wanted. In the search page, I built simple forms pointing to my results pages (this might not be doable if you must implement google branding), and in the results page I put the following:
(in the <head>)
<script src="http://www.google.com/jsapi"></script>
<script>
// This function extracts the query from the URL (if GET) or builds a search query.
// Code removed to simplify the example.
function buildQuery () {
return '<?php echo $_POST['q'];?> more:pagemap:metatags-taxonomias:News'); // injecting the taxonomy metatag filter
}
google.load('search', '1', {language : 'es'});
google.setOnLoadCallback(function() {
var customSearchOptions = {};
customSearchOptions[google.search.Search.RESTRICT_EXTENDED_ARGS] = {'sort':'metatags-pubdate:d,metatags-pubdate:r:<?php echo $_POST['startdate'];?>:<?php echo $_POST['enddate'];?>'}; // these come from the POST request, are processed earlier in the script.
var customSearchControl = new google.search.CustomSearchControl('XXXXXXXXXXX', customSearchOptions); // Put your own App key here.
customSearchControl.setResultSetSize(google.search.Search.FILTERED_CSE_RESULTSET);
var drawOptions = new google.search.DrawOptions();
drawOptions.enableSearchResultsOnly(); // I don't want the search box here
customSearchControl.draw('cse-results-press', drawOptions);
var query = parseQuery();
if (query) {
customSearchControl.execute(query);
}
}, true);
</script>
In the <body>:
<div id="cse-results-press">Loading...</div>

WHMCS Addon Module with Multiple Pages

I am currently working on developing my first WHMCS Addon Module, and so far everything has gone very well. However, I need to make multiple content pages, and the only way to display output according to the Wiki Article is to echo it in the output function. How can I create individual pages when the only way to display content is via a single PHP function?
I am assuming using divs, and hiding the relevant divs, although not exactly the best method. It says you can use the "modulelink" variable to link back to the module, but I have no idea how to use this, or if it can be used for making multiple content pages.
http://docs.whmcs.com/Addon_Module_Developer_Docs
After some tinkering, it was much more simple than I realized, and the "modulelink" variable was just so you could link back to the page. To create additional pages, you can basically just do something along the lines of...
Datacenters
Then in the output function have...
$category = $_GET['catid'];
if ($category == "1" || $category == "")
{
//page 1 content here
}
else if ($category == "2")
{
//page 2 content here
}
and so on.

Filtering results and pagination

I have a template that shows a filter form and below it a list of the result records. I bind the form to the request so that the filter form sets itself to the options the user submitted when the results are returned.
I also use pagination. Using the code in the pagination documentation means that when the user clicks for the next page, the form data is lost.
What is the best way of dealing with pagination and filtering in this way?
Passing the querystring to the paginiation links.
Change the pagination links to form buttons and therefore submit the filter form at the same time, but this assumes that the user hasn't messed about with the filter options.
As above but with the original data as hidden fields.
ALJ
If you don't mind tweaking your URLs a bit you can embed the filter options directly into the URL. That actually has the very nice side benefit of making search options bookmarkable. Thus when a user clicks the next/prev buttons on the pagination then all the information will be carried forward from the URL.
You may have to split your URLs for that page up a tad though. If you've used some keyword args to the view function though, you can put all the logic for that view in one function.
Quick example
in your urls.py
urlpatterns = patterns('',
(r'^some_url/to/form/$', 'myapp.myview'),
(r'^some_url/to/form/(?P<filter>[^/]+)/$', 'myapp.myview'),
)
then in your view.py
def myview(request, filter=None):
if request.method == 'POST':
# if the form is valid then redirect the user to the URL
# with the filter args embedded
elif filter is None:
form = MyForm() # init it with the blank defaults
elif filter is not None:
# Convert your filter args to a dict
data = {'my_form_field' : filter}
form = MyForm(data)
else:
# Sanity checking just in case I missed a case. Raise an exception.
# the rest of the display logic for the initialized form
There are certainly cases where using a solution like this does not apply, but without knowing more about your specific case, I can't say.
Hope this helps!