So there are instances where there could be one or more errors to report to the user (and to notify me about) that could be caused at a controller level (input, validation) or model level.
I'm considering creating a basic helper for 'feedback' that basically has global message arrays (notice, error, success)
Then either at the model or controller level, if something goes wrong (or right!), I can call the feedback function.
feedback('error','Connection is temporarily down blah')
I won't need to pass it through to my views as it will be globally set so I can just call something like $this->feedback->display_all().
Is this an ok/MVC friendly way to do things? It seems like a straight-foward method for me to implement
For my project, I created a tiny mdl_error model.
This model has one public function, throwError, and some private helpers that will show flash notices to the user and send an email to me with current values and session data if need be. The model is autoloaded and is only called if needed.
Here is basically what it looks like:
<?php
class mdl_error extends CI_Model
{
//types: error, alert, good
function throwError($type, $message, $info="", $flash=true, $email=true)
{
if($flash){
$alert = $type."|".$message;
$this->session->set_userdata(array("flash" => $alert));
}
if($email){
$problems = $this->recursivePrintingOfVariables($info);
$sessionData = $this->recursivePrintingOfVariables($this->session->userdata);
$emailMessage = "Name<br/> <br/>Something has happened. <br/> <br/>";
$emailMessage .= "The type was: {$type}<br/>The message was: {$message}<br/> <br/> <br/>";
$emailMessage .= "Here is the local variables at the time:<br/> <br/>{$problems}<br/> <br/> <br/>";
$emailMessage .= "Here is the session data:<br/> <br/>{$sessionData}<br/> <br/> <br/>";
$emailMessage .= "Please solve this problem or we are all dooooooomed.<br/><br/>Love,<br/>Website";
$this->load->library('email');
$this->email->from("my email");
$this->email->to("error#whatever.com");
$this->email->subject($type.' Message from Website');
$this->email->message($emailMessage);
$this->email->send();
}
}
function recursivePrintingOfVariables($info)
{
$keys = array_keys($info);
$string = "";
foreach($keys as $key){
$string .= $key." => ";
if(is_array($info[$key])){
$string .= "Inner Array<br/>";
$string .= "<div style='margin-left:15px;'>";
$string .= $this->recursivePrintingOfVariables($info[$key]);
$string .= "</div>";
}else{
$string .= $info[$key];
}
$string .= "<br/><br/>";
}
return $string;
}
}
Then, if there is a spot in my code where an error happens, I just call:
$this->mdl_error->throwError("error","something happend", get_defined_vars());
If you use the show_error('Your error message'); function in your controller you will achieve the same. If you want to customise the look of the error you need to work with the error_general.php file found in applications/errors.
If you also want to log the errors you could use the log_message('level', 'message'); function in your controller.
Related
With Laravel Blade, is there an elegant way to check if a view exists before doing an #include?
For example I'm currently doing this:
#if(View::exists('some-view'))
#include('some-view')
#endif
Which gets quite cumbersome when 'some-view' is a long string with variables inside.
Ideally I'm looking for something like this:
#includeifexists('some-view')
Or to make #include just output an empty string if the view doesn't exist.
As an aside, I would also like to provide a set of views and the first one that exists is used, e.g.:
#includefirstthatexists(['first-view', 'second-view', 'third-view'])
And if none exist an empty string is output.
How would I go about doing this? Would I need to extend BladeCompiler or is there another way?
Had a similar issue. Turns out that from Laravel 5.3 there is an #includeIf blade directive for this purpose.
Simply do #includeIf('some-view')
I created this blade directive for including the first view that exists, and returns nothing if none of the views exist:
Blade::directive('includeFirstIfExists', function ($expression) {
// Strip parentheses
if (Str::startsWith($expression, '(')) {
$expression = substr($expression, 1, -1);
}
// Generate the string of code to render in the blade
$code = "<?php ";
$code .= "foreach ( {$expression} as \$view ) { ";
$code .= "if ( view()->exists(\$view) ) { ";
$code .= "echo view()->make(\$view, \Illuminate\Support\Arr::except(get_defined_vars(), array('__data', '__path')))->render(); ";
$code .= "break; } } ";
$code .= "echo ''; ";
$code .= "?>";
return $code;
});
I want to show filter of category on submenu, my code works!!
My problem is that if page are already filtered, my code does not return the options
I believe it has to do something in the code that bypasses the filter page and again bring the options in the submenu even if already have the filter on page
HTML of submenu:
{{block type="core/template" category="3" template="page/html/icons_submenu.phtml"}}
Content of page icons_submenu.phtml:
<?php
$layer = Mage::getModel("catalog/layer");
$category = Mage::getModel('catalog/category')->load($this->getCategory());
$layer->setCurrentCategory($category);
$attributes = $layer->getFilterableAttributes();
foreach ($attributes as $attribute) {
if ($attribute->getAttributeCode() == 'color') {
$filterBlockName = 'catalog/layer_filter_attribute';
$result = Mage::app()->getLayout()->createBlock($filterBlockName)->setLayer($layer)->setAttributeModel($attribute)->init();
echo '<strong>Color:</strong><br />';
foreach($result->getItems() as $option) {
echo ' ' . $option->getValue() . ' - ' . $option->getLabel() . '<br />';
}
}
}
?>
Example:
I would really suggest you to actually move all that logic into a proper module, a proper block and a proper model and not in a template like you are doing right now.
If you actually want further help for that, feel free to ask, making something according to the coding guide lines of Magento would make you even happier of your job, I can assure you.
This being said, what you actually want is a current filter model based on the current category and a specify attribute.
You don't need to go by the block catalog/layer_filter_attribute in a way to do this, you can directly go by the model based on the layer you already load.
So, this way of doing it should work, although it should not be in a template or view, once again :
<?php
$category = Mage::getModel('catalog/category')
->load($this->getCategory());
$layer = Mage::getModel('catalog/layer')
->setCurrentCategory($category);
$attributes = $layer->getFilterableAttributes();
foreach ($attributes as $attribute) {
if ($attribute->getAttributeCode() == 'color') {
// $filterBlockName = 'catalog/layer_filter_attribute';
/** This is actually your only problem in your code **/
// $result = Mage::app()->getLayout()->createBlock($filterBlockName)->setLayer($layer)->setAttributeModel($attribute)->init();
/** But would work with this line **/
$result = Mage::getModel('catalog/layer_filter_attribute')
->setLayer($layer)
->setAttributeModel($attribute);
echo '<strong>Color:</strong><br />';
foreach($result->getItems() as $option) {
echo ' ' . $option->getValue() . ' - ' . $option->getLabel() . '<br />';
}
}
}
?>
Then you can see it still works based on only the colours I have in the current category
But also when the category is already filtered on a specific colour
I'm gathering information on the location of stores.
The search is:
<?php
...
$url='https://maps.googleapis.com/maps/api/place/search/json?key=[my_key]&location=40.420989,-3.706812&radius=1000030&=&sensor=false';
$body=file_get_contents($url);
...
?>
I return a Json without problems, and indicates that there is another page of results.
I'll be back to make another call as follows
<?php
...
$url2='https://maps.googleapis.com/maps/api/place/search/json?key=[my_key]&pagetoken=ClREAAAAQXKNHPVGCkTC_MdjSqi2T0KBDMWjEu4KF1Ylw1761Po-67AnNSp4zw0wXD4oocGpx4olSl4k2LyklJBl3mBF4VPmxp3IoOCHDRlXVmivaDsSEBuG_V1GvCH1gS5s0LCuy3EaFNXxUzzHhZ5ZRYNfeGHuqewR6Zk7&sensor=false';
$body=file_get_contents($url2);
...
?>
If I run it with the second call I get an error
'status' -> INVALID_REQUEST
But when I paste the ulr2 browser in the result is correct.
How I can fix it?
Thanks
It has something to do with the timing between the requests, if you run them immediately one after the other, the pagetoken isn't valid yet, you have to wait a few seconds between consecutive requests.
This is because google's license terms don't allow you to fetch all the results at once and return them all at once to the user. You should have a user action asking for more results, which adds a delay of a couple of seconds.
sleep(2) between requests will solve the problem
Sleep(1.3) is the shortest amount of time that seemed to work. In other words the next page token becomes active about 1.3 seconds after it is returned in the previous API request.
Please try below code, I have used sleep(2) function for delay between requests, because next pagetoken needs to be validated on google server.
You can even use looping to remove code repetation.
// your query here
$query = "";
// api key here
$api_key = "";
// api call code
try {
echo $url = "https://maps.googleapis.com/maps/api/place/textsearch/json?query=" . $query . "&location=40.420989,-3.706812&radius=1000030&=&sensor=false&key=" . $api_key;
echo "<br>";
$result = file_get_contents($url);
$query_results = json_decode($result, true);
echo "First set" . "<br>";
print_r($query_results);
$next_page_token = $query_results['next_page_token'];
unset($query_results);
$query_results = array();
sleep(2);
echo $url = "https://maps.googleapis.com/maps/api/place/textsearch/json?query=" . $query . "&location=40.420989,-3.706812&radius=1000030&=&sensor=false&key=" . $api_key . "&pagetoken=" . $next_page_token;
echo "<br>";
$result = file_get_contents($url);
$query_results = json_decode($result, true);
echo "Second set" . "<br>";
print_r($query_results);
$next_page_token = $query_results['next_page_token'];
unset($query_results);
$query_results = array();
sleep(2);
echo $url = "https://maps.googleapis.com/maps/api/place/textsearch/json?query=" . $query . "&location=40.420989,-3.706812&radius=1000030&=&sensor=false&key=" . $api_key . "&pagetoken=" . $next_page_token;
echo "<br>";
$result = file_get_contents($url);
$query_results = json_decode($result, true);
echo "Third set" . "<br>";
print_r($query_results);
unset($query_results);
$query_results = array();
} catch (Exception $e) {
$e->getCode();
$e->getLine();
}
The first query will generate 2nd page token.
You just add "&pagetoken=tokenvalue" in your uri.
Sure it work. No alternative option.
I have been dealing with a problem for a while. How can I set the validation errors using redirect in a function? This is the code I have in my controller :
function send()
{
$this->form_validation->set_rules('e-mail', 'Email', 'trim|required|valid_email');
$this->form_validation->set_rules('cellphone', 'Cellphone Number', 'trim|required|is_natural');
$this->form_validation->set_message('required', '%s is required.');
$this->form_validation->set_message('valid_email', '%s is not a valid Email Address');
$this->form_validation->set_message('is_natural', '%s can only contain numbers.');
$this->form_validation->set_error_delimiters('<li>', '</li>');
if($this->form_validation->run() == FALSE)
{
redirect ('/');
}
else
{
echo '<pre>';
print_r($_POST);
echo '<pre>';
}
}
And this is the code I use in my view file:
<? if (validation_errors())
{
echo '<div id="validation_errors" title="Error:">';
echo '<div class="response-msgs errors ui-corner-all"><span>Errors:</span><br /><ul>';
echo validation_errors();
echo '</ul></div>';
echo '</div>';
}
?>
I found the way to do it. Redirecting does not keep the data to be shown. I used the code below to solve the problem:
if($this->form_validation->run() == FALSE)
{
$this->index();
}
I know it's a bit late but this method works wonders for me.
If you are validating your form in a different function than the one the form is loaded in, you can send your validation_errors() to any page that you redirect() by passing the validation_errors() method to $this->session->set_flashdata() like so:
if ($this->form_validation->run() == FALSE) {
$this->session->set_flashdata('error', validation_errors());
redirect('/');
}
In your controller functions where you would like your errors or messages to be received you can then set them to the $data array like so:
if (!empty($this->session->flashdata('message'))) {
$data['message'] = $this->session->flashdata('message');
} elseif (!empty($this->session->flashdata('error'))) {
$data['error'] = $this->session->flashdata('error');
}
At the top of my views I usually include:
<?php if (isset($message)) {
echo '<p class="alert alert-info">'.$message.'</p>';
} elseif (isset($error)) {
echo '<p class="alert alert-danger"><strong>Error: </strong>'.$error.'</p>';
}?>
Using twitter bootstrap classes to format the messages helps to differentiate them.
I included the message flashdata so that you can see how whatever type of message or error you want to send, you are able to format them differently for all information, warning, success and error messages.
As per my comment:
function index()
{
$this->load->library('form_validation');
$data = array
(
'Param' => 'Value'
);
if($this->input->post('cellphone', true) !== false)
{
if($this->form_validation->run() != FALSE)
{
echo '<pre>' . print_r($_POST, true) . '</pre>';
}
}
$this->load->view('index', $data);
}
First, you need to change your form so it points to the current page, i.e. current_url() or site_url('controller/index').
When you go to the index without posting, it will simply skip the validation. Upon submitting your form, it will run the validation.
You can then use the built in form_error or validation_errors methods to display the errors within your index view.
finally i got a solution for the redirect with validation_errors
This is using to pass the validation_errors in the session data,
i do it like this
if ($this->form_validation->run()) {
$data = array(
'email' => $this->input->post('email'),
'is_login' => true
);
$this->session->set_userdata($data);
redirect($this->input->post('redirect'));
}
else
{
$data = array (
'errors' => validation_errors(),
'is_login' => false
);
$this->session->set_userdata($data);
redirect($this->input->post('redirect'));
}
and in the page i used
$this->session->set_flashdata('redirectToCurrent', current_url());
if ($this->session->userdata('is_login'))
{
echo "You are using this as : </br>" . $this->session->userdata('email');
echo "</br><a href='/logout'>Logout</a>";
}
else
{
echo form_open('login');
echo "" . $this->session->userdata('errors');
echo "email : " . form_input('email');
echo "Password : " . form_password('password');
echo form_submit('submit', 'Login');
echo form_hidden('redirect',$this->uri->uri_string());
echo form_close();
}
I wish you like this fast solution
Hi. I have the form in which I use form_validation If the user make any mistake (leave some required fields empty), user is redirected back to the form, and the form has been re-populated. All works, except my select , which is generated from the database.
Here is my code from the view:
echo "<select name='parentid'" . set_value("parentid"). ">";
echo '<option value = "0">None</option>';
foreach ($faq_categories as $row => $option) {
echo "<option value=" . $option['catid'] . ">" . $option['categoryname']. "</option>";
}
echo '</select>';
Here is my controller code:
public function displayAddFaqCategoryForm($error = null)
{
$data['title'] = "Add new FAQ Category";
$data['main_content'] = 'addFaqCategory';
$selectWhat = array('tname' => 'faq_categories',
'sortby'=> 'catid',
'how' => 'asc'
);
$this->load->model('selectRecords');
$data['faq_categories'] = $this->selectRecords->selectAllRecords($selectWhat);
$this->load->vars($data);
$this->load->view('backOffice/template');
} // end of function displayAddFaqCategoryForm
And here is the model code:
public function selectAllRecords($selectWhat = array())
{
$data = array();
$tname = $selectWhat['tname'];
$sortby = $selectWhat['sortby'];
$how = $selectWhat['how'];
$this->db->order_by($sortby,$how);
$query = $this->db->get($tname);
if($query->num_rows() > 0)
{
foreach($query->result_array() as $row)
{
$data[] = $row;
}
}
$query->free_result();
return $data;
} // end of function selectAllRecords
I am not getting any error messages, just the select is not repopulated with last used. Any help will be deeply appreciated.
You're using set_value() incorrectly
echo "<select name='parentid'" . set_value("parentid"). ">";
It's meant to output the actual value (for text inputs). This would produce something like:
<select name='parentid'ActualValue>
Which is not how a <select> element is populated, and is invalid HTML. See the correct usage in the Form Helper docs.
You can use set_select(), and it goes on your <option>:
foreach ($faq_categories as $row => $option) {
echo "<option value=".form_prep($option['catid']).'"';
echo set_select('parentid', $option['catid']); // outputs selected="selected"
echo ">".html_escape($option['categoryname'])."</option>";
}
I've taken a few other liberties with your code here as you can see, to be on the safe side (always).
If this is too much of a mess, you might be interested in the form_dropdown() function.