Symfony 2: parsing input value from a twig for a simple search function - search

I come back with a symfony2 problem I have.
I'm trying to make a really simple "search form" to display some posts of a blog. To not overkill it, I've decided to create the form directly in the twig like this:
<form class="form-search" method="post" action="{{ url('search_route') }}">
<input type="text" placeholder="Search" class="input-medium search-query" name="search">
<button type="submit"><img src="/img/search.png" alt="search" /></button>
</form>
In my controller I'm trying to find a way of how to pass the value of the input in my query. Here is the code of the searchAction():
use Symfony\Component\HttpFoundation\Request;
/..
public function searchAction(Request $request)
{
$data = $request->request->all();
$dql = "SELECT a FROM PfBlogBundle:Article a WHERE a.title LIKE '{$data['search']}' ORDER by a.id DESC";
$query = $em->createQuery($dql);
$paginator = $this->get('knp_paginator');
$pagination = $paginator->paginate(
$query,
$this->get('request')->query->get('page', 1)/*page number*/,
4/*limit per page*/
);
return $this->render('PfBlogBundle:Default:blog.html.twig', array('pagination'=>$pagination));
}
the fact is that if I print_r($data), I have the value sent through the input.. My problem is really to pass it in the query I think.. I'm developing locally and get a server error in the browser when I hit submit :/
Any idea?

Related

Get an html attribute in node.js

Say I have a button like
<form action="/action1" method ="post">
<button type="submit" id="button1"> Click Me </button>
</form>
I want to get a value stored in the attributes (in this case "id") and do something like this
app.post('/action1', function (req, res) {
var buttonId = req.id // this is the part I dont understand
});
how would I fill in the line
var buttonId =
Thank you
The posted data does not include the id attribute. You can set the name and/or the value attribute and those will be part of the POST request. e.g.
<button type="submit" name="button1">Click Me</button>
To get to the posted data in express look here: Express js form data

Angularjs + Laravel Stripe integration - Response goes to server and other details missing

i have an Angular Storefront app set up. I have a shopping cart functionality in place and a stripe "pay with card" button etc. pretty much looks like this:
<form action="/#/order" method="POST">
<script
src="https://checkout.stripe.com/checkout.js" class="stripe-button"
data-key="{{ stripeApiKey }}"
data-billingAddress=true
data-shippingAddres=true
data-amount="{{ amount }}"
data-name="StoreFront Name"
data-description="Custom-Made Jewellery"
data-image="../images/www/logo.png"
data-locale="auto">
</script>
</form>
Evrything up to this point is working fine. I submit the form and stripe returns the token but the form goes to the server following the route localhost/order (without the # symbol) instead of angular's localhost/#/order.
Why is stripe forcing this redirect? In other words why isn't angular capturing this return call?
Anyways. Then I create a route with Laravel to capture this and dump to inspect the returned data like so:
Route::post('/order', function($request){
dd($request);
});
Yep, data captured by stripe-generated form is returned except amount is missing... I mean everything including stripeToken, buyer's details such as: Name, Email, Billing and Shipping address are returned BUT detail regarding the amount is missing.
Is this normal or I'm I missing something?
Lastly currency is still showing the default: Where can I change currency from say USD to GBP?
Thanks in advance
1/ I don't think Checkout is forcing the redirect, but I don't know enough about Angular to explain what's going on, sorry.
2/ Yes, this is normal. The amount passed to Checkout in the data-amount configuration option is used for display purposes only. The actual amount that is charged is the one you pass in the amount parameter in the charge creation request in your server-side code.
If you need the amount to be user-specified (for instance, if you're taking donations), you'll need to add the amount to the form. Here is a simple JSFiddle to illustrate this case: https://jsfiddle.net/ywain/g2ufa8xr/
3/ You can use the data-currency parameter to change the currency displayed in the Checkout form. Just like data-amount, this is for display purposes only and the actual currency used for the charge is specified by the currency parameter in the charge creation.
This is what i managed to do.
I went with the custom form approach. I had a form template to capture both customer and card inputs in billing.template.html like so:
<form method="POST" id="payment-form">
<span class="payment-errors"></span>
<div>
<label>Name</label>
<input type="text" name="name" data-stripe="name">
</div>
<div>
<label>Email</label>
<input type="text" name="email" data-stripe="address_email">
</div>
<div>
<label>Address Line 1</label>
<input type="text" name="street" data-stripe="address_line1">
</div>
<div>
<label>Postcode</label>
<input type="text" name="postcode" data-stripe="address_zip">
</div>
<div>
<label for="country">Country</label>
<select ng-include="'../templates/_partials/_countrylist.html'"
id="countries" name="country" class="form-control"
name="country" ng-model="country" id="country" size="2"
data-stripe="address_country" required></select>
</div>
<div class="form-row">
<label>
<span>Card Number</span>
<input type="text" name="cardNumber" size="20" data-stripe="number"/>
</label>
</div>
<div class="form-row">
<label>
<span>CVC</span>
<input type="text" name="cvc" size="4" data-stripe="cvc"/>
</label>
</div>
<div class="form-row">
<label>
<span>Expiration (MM/YYYY)</span>
<input type="text" name="expMonth" size="2" data-stripe="exp-month"/>
</label>
<span> / </span>
<input type="text" name="expYear" size="4" data-stripe="exp-year"/>
</div>
<button id="customButton">Pay with Card</button>
</form>
I know we are not supposed to use name attribute in those form inputs but i left them so i could use angular validation, but i remove them using jquery before submitting to server.
Now i created a controller to handle the form: BillingController.js. In there i had an "on click" handler which kick started things by getting a hold of the form and doing some preparatory work: disabling button to prevent further clicks and removing those 'dreaded' name attributes, comme ca:
$('#customButton').on('click',function(event) {
var $form = $('#payment-form');
// Disable the submit button to prevent repeated clicks
$form.find('button').prop('disabled', true);
//NOW REMOVE THOSE NAME ATTRIBUTES
$form.find('input').removeAttr('name');
// call Stripe object and send form data to get back the token.
// NOTE first argument is $form
Stripe.card.createToken($form, stripeResponseHandler);
// Prevent the form from submitting with the default action
return false;
});
Now let me quote the documentation here as this is very important to understand: https://stripe.com/docs/tutorials/forms
The important code to notice is the call to Stripe.card.createToken.
The first argument is the form element containing credit card data
entered by the user. The relevant values are fetched from their
associated inputs using the data-stripe attribute specified in the
form.
Next we create stripeResponseHandler(). Remember it was the second argument in Stripe.card.createToken($form, stripeResponseHandler); above which gets called when Stripe returns the token.
function stripeResponseHandler(status, response) {
var $form = $('#payment-form');
if (response.error) {
// Show the errors on the form
$form.find('.payment-errors').text(response.error.message);
$form.find('button').prop('disabled', false);
} else {
// response contains id and card, which contains additional card details
var token = response.id;
// Insert the token into the form so it gets submitted to the server
$form.append($('<input type="hidden" name="stripeToken" />').val(token));
// and submit
$form.get(0).submit();
}
};
This is copy and paste stuff from stripe's own documentation: https://stripe.com/docs/tutorials/forms. Now, I want to say that, this is where a lot of us were tripping over the fact that form was performing a redirect etc. - notice final line $form.get(0).submit(); . Thats what caused the auto submit, redirecting to what ever action was on form, if u had any (in my case action attribute wasn't necessary as i was doing redirects in my controller).
So i decided to remove $form.get(0).submit() and implemented my own redirect after i was done sending data to the server.
NOTE: Stripe's response will have included data from the $form - try console.log(response); to have an idea of what's being posted back.
FINALLY:
We check if there were any errors returned and if so display them. Otherwise its all good, send data to the server.
The final code looks like:
function stripeResponseHandler(status, response) {
var $form = $('payment-form');
if (response.error) {
// Show the errors on the form
$form.find('.payment-errors').text(response.error.message);
} else {
// response contains id and card, which contains additional card details
var token = response.id;
// prepare data
var data = {
stripeToken: token,
fullName: response.card.name,
street: response.card.address_line1,
postcode: response.card.address_zip,
town: response.card.address_city,
country: response.card.address_country,
last4: response.card.last4
};
// send to server
$http.post('/checkout', data).then(function(result){
// here you can redirect yourself.
window.location.href = "/#/order-complete";
});
}
};
Angular really playing well with stripe here. Check out this link also: https://gist.github.com/boucher/1750368 - learn a lot from it.
I hope it helps someone today. Happy coding!
Stripe doesn't get involved with your form aside from preventing the default action on form submit event and stopping event propagation. Once the checkout process completes, it appends the relevant data to your form and then triggers a form submit event that is handled by HTML / Javascript natively.
I recommend using something like https://github.com/tobyn/angular-stripe-checkout to get your Stripe response handled correctly by Angular.
Otherwise you could add ng-submit="handleStripeCheckout($event)" to your form instead of action="/#/form". When Stripe's checkout process completes, your $scope.handleStripeCheckout method will be run and you can analyze the new form data inside that method.
Edit: Stripe checkout.js actually triggers form.submit(). That's a pretty bad bug on their part considering that almost no browsers handle that correctly. (Form submitted using submit() from a link cannot be caught by onsubmit handler)

Submit Html form with a specific model

I am using MVC, Razor, Knockout, typescript in my application. Need to prepare a excel file with a model/dataset that is set at client side. I am using the following code but the problem i understand is that the dataset is being taken as string and not exactly like the model it is supposed to take as. Here is the code
This is observable (dataset)
vmExportData = ko.observableArray<ExcelModel>([]);
Push data to the dataset
vmExportData.push(new ExcelModel (
e.sender.data()[i].Id,
e.sender.data()[i].Name,
e.sender.data()[i].Address1,
));
Html code, to prepare excel file (at the controller)
#using (Html.BeginForm("ExportToExcel", "MyController", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div data-bind="css:{hidden: true}">
<input type="text" data-bind="value: vmExportData" />
</div>
<input class='excelIcon' type="submit" data-ux-access="true" value="">
}
Controller method :
public ActionResult ExportToExcel(ExportModel exportModel)
{
The problem is, the exportModel parameter value is coming as null ! Any other way out to get it to work ? Is there a way to make excel at client side and render as well ?
Try setting name="exportModel" on your tag.
Also make sure to have an [HttpPost] on top of your controller action, since your form method is post.
What does the ExportModel class look like?

$wpdb not seeming to work with

I'm using the following code to populate a WordPress dropdown menu with all the unique values from a custom field:
<form name="search" action="" method="get">
<select name="stateprov">
<option>Select...</option>
<?php
$metakey = 'state_prov';
statesProvs = $wpdb->get_col($wpdb->prepare("SELECT DISTINCT meta_value FROM $wpdb->postmeta WHERE meta_key = %s ORDER BY meta_value ASC", $metakey) );
if ($statesProvs) {
foreach ($statesProvs as $stateprov) {
echo "<option value=\"" . $stateprov . "\">" . $stateprov . "</option>";
}
}
?>
</select>
<input type="submit" value="search" />
</form>
However, it takes nothing from the DB so the popup list is empty.
Trying a different query like
$statesProvs = $wpdb->get_col( "SELECT ID FROM $wpdb->posts WHERE post_author = 2" );
Works as expected. I get a popup with a bunch of Post ID's in it. But the query that's supposed to work on my custom metadata just brings up an empty menu (and print_r reveals an empty array).
The data is definitely in the DB... what am I doing wrong?
It may also be significant that I'm using a custom metabox PHP class that writes all the custom-created field keys and values into the value of the _custom_meta metakey. If I've put that correctly. Thus:
a:61:{s:10:"state_prov";s:2:"CA";s:13:"vertical_drop";s:13:"3100ft / 945m";s:14:"base_elevation";s:14:"7953ft / 2424m";s:16:"summit_elevation";s:15:"11053ft / 3369m";s:12:"skiable_area";s:10:"3500 acres";s:16:"average_snowfall";s:14:"400in / 1016cm";s:13:
Etc. Is this way of storing the custom metadata preventing wpdb from accessing it properly?
Thanks!
you must use global $wpdb before you start the wpquery, refer codex for more details
<form name="search" action="" method="get">
<select name="stateprov">
<option>Select...</option>
<?php
global $wpdb;
$metakey = 'state_prov';
$wpdb->get_col($wpdb->prepare("SELECT DISTINCT meta_value FROM $wpdb->postmeta WHERE meta_key = %s ORDER BY meta_value ASC", $metakey) );
if ($statesProvs) {
foreach ($statesProvs as $stateprov) {
echo "<option value=\"" . $stateprov . "\">" . $stateprov . "</option>";
}
}
?>
</select>
<input type="submit" value="search" />
</form>
Etc. Is this way of storing the custom metadata preventing wpdb from
accessing it properly?
Yes.
In general, Wordpress isn't capable of searching or sorting through serialized PHP arrays.
The most viable solution here is to store this data point ('state_prov') in its own custom field. This will allow you to naturally search and sort using $wpdb. It also seems like you want this custom field to be private/hidden, in which case you'll want to prefix it with an underscore: '_state_prov'.
Another option, assuming the above isn't viable for your needs, is to use MySQL String Functions; but this is far from ideal or optimized (especially if the database grows significantly.) Rough, untested example below.
// Assuming all state_prov are exactly 2 characters in length
$identifier = 's:10:"state_prov";s:2:"';
$identifier_length = strlen($identifier);
$wpdb->get_col($wpdb->prepare("SELECT DISTINCT(SUBSTR(`meta_value`, INSTR(`meta_value`, '$identifier')+$identifier_length, 2)) as `state_prov` FROM $wpdb->postmeta WHERE `meta_key`=%s ORDER BY `state_prov` ASC", $metakey) );

Disable empty search

I have a photography site driven in part by the 'Photoshelter' service, and I put an embedded search bar in my nav.
<form action="http://brettcole.photoshelter.com/search" method="get">
<input type="text" placeholder="search library" size="15" name="I_DSC">
<input type="submit" value="go">
<input type="hidden" name="I_DSC_AND" value="t">
<input type="hidden" name="_ACT" value="search">
</form>
It allows for a search to be executed with the no search term present, which then returns all 12,000 photos in my archive. Is there a best practice for preventing this, such that the user has to type something or nothing will happen when they click search?
It's also present on my advanced search page. This is generated by a search widget shortcode in the Photoshelter back end. I'd like to apply the same thing here, but not sure how the widgetization of it might affect the process.
Many thanks
You can use the onsubmit attribute of the form element to check if the user has entered information in any fields and then prevent submit based on that.
<script>
function checkValues() {
searchBox = document.getElementById("SearchField");
return searchBox.value != ""; // True will allow submission and false will prevent it
}
</script>
With this...
<form onsubmit="checkValues();" action="http://brettcole.photoshelter.com/search" method="get">
<input type="text" id="SearchField" placeholder="search library" size="15" name="I_DSC">
<input type="submit" value="go">
<input type="hidden" name="I_DSC_AND" value="t">
<input type="hidden" name="_ACT" value="search">
</form>
Should do what you need.
See also this answer: How to grab the onSubmit event for a form?
The actual search isn't working
From the contact page for example, it returns this
http://brettcolephotography.com/contact.html?I_DSC=red&I_DSC_AND=t&_ACT=search
the formula for my search returns is
http://brettcole.photoshelter.com/search?I_DSC=red&I_DSC_AND=t&_ACT=search
this search bar is present on all three of my web properties, personal site, blog, and photoshelter site, all three are tightly integrated to where you can't tell when you're switching between them. It needs to work regardless of where the search is being executed from. Thanks
Here is a function I wrote to disable the search form submitting if the search field is empty. It also focuses the cursor on the search field if the form is not submitted so that the user does not think that search is broken.
This is assuming that jQuery is loaded. Hope this helps!
var preventSearchIfEmpty = function() {
$('form[method="get"]').on( 'submit', function( ev ){
var query = $('input[type="text"]').val(),
queryLength = query.length;
if ( 0 === queryLength ) {
// Make the cursor blink so user is aware it's not broken, they need input to search
$('input[type="search"]').focus();
ev.preventDefault();
return;
}
});
}();

Resources