I am using JQuery Datatables to display all the users of my forum in a page from a table called 'users'. I am using Code Igniter for server-side processing. I looked up the Datatables documentation on how to render data and I got it going. However, I have some PHP variables and functions that I want to echo into a JavaScript variable so I can use them within the render function for the target column. However, I am wondering how to echo a model function from a different table with a parameter into the JavaScript variable, or whether that is even a good approach. Below is my set-up. I have only posted the lines that are relevant to this question (unless I am doing it all wrong).
Model (Dashboard_model)
public function my_followee($followee) {
$username = $this->session->username;
return $this->db->get_where('followers', array('user' => $followee, 'follower' => $username))->row_array();
}
JS:
{
"targets": [-1],
"data": null,
"orderable": false,
"render": function ( data, type, full ) {
var myself = "<?php echo $this->session->username; ?>";
var is_following = "<?php echo $this->dashboard_model->my_followee('+full[1]+');" //full[1] is username column
if(is_following == false && myself !== full[1]) {
return Follow ';
} else if(is_following == true && myself !== full[1]) {
return Unfollow ';
} else if(myself == full[1]) {
return 'This is you';
}
}
}
Basically, I want to check if the loggedin user is already following the currently selected user. If no, show follow button, otherwise, show unfollow button.
When I check the page, all the buttons have follow (except the currently logged in user), whereas some should return true and show unfollow. I suspect I'm violating syntax rules in this line:
var is_following = "<?php echo $this->dashboard_model->my_followee('+full[1]+');"
I am new to JQuery and Datatables and can't seem to figure out the right way to do this. Please can someone point me to the right direction, or suggest better ways of achieving my aim? I would really appreciate the help.
EDIT
I tried this in my controller that handles the ajax call request
public function users_ajax_list()
{
$this->load->model('user/users_model', 'users');
$this->load->model('dashboard_model');
$list = $this->users->get_users();
$data = array();
foreach ($list as $user) {
if ($this->dashboard_model->my_followee($user->username) == true) {
$is_following = true;
} else {
$is_following = false;
}
$row = array();
$row[] = $user->profile_image;
$row[] = $user->username;
$row[] = ucfirst($user->surname);
$row[] = ucfirst($user->first_name);
$row[] = ucfirst($user->state);
$row[] = ucfirst($user->lga);
$row[] = $user->followers;
$row[] = $user->following;
$row[] = $user->total_posts;
$row[] = $user->gender;
$data[] = $row;
}
$output = array(
"draw" => $_POST['draw'],
"recordsTotal" => $this->users->count_all_users(),
"recordsFiltered" => $this->users->count_filtered_users(),
"data" => $data,
);
//output to json format
echo json_encode($output);
echo json_encode($is_following);
}
It produced errors: wrong JSON response. What am I doing wrong? And how is the best way to do it? And how can I access the JSON variable within the render function for the targeted column.
Related
I have a simple dropdown field with 2 values and a dependent dropdown field:
public function areaForm() {
$datasource = function($val) {
if ($val =='yes') {
$areas = DataObject::get('Area', 'ParentID = 0');
return $areas->map('ID', 'Name');
}
if ($val == 'no') {
return false;
}
};
$fields = new FieldList(
TextField::create('Name', 'Area Name:'),
$dropField = DropdownField::create('isChild', 'Is this a sub Area?', array('yes' => 'Yes', 'no'=>'No' ))
->setEmptyString('Select one'),
DependentDropdownField::create('ParentSelect', 'Select Parent Area:', $datasource)
->setDepends($dropField)
->setEmptyString('Select one')
);
return new Form($this, __FUNCTION__, $fields, FieldList::create(new FormAction('doSaveArea', 'Save area')));
}
public function doSaveArea($data, $form) {
var_dump($data);
exit;
$name = $data['Name'];
$isChild = $data['isChild'];
if ($isChild === 'no') {
$area = new Area();
$area->Name = $name;
$area->ParentID = 0;
$area->write();
}
elseif ($isChild === 'yes') {
$area = new Area();
$area->Name = $name;
$area->ParentID = $data['ParentSelect'];
$area->write();
}
$this->redirectBack();
}
When ever I try to save my object by submitting the form, it gives me the same message:
Please select a value within the list provided. x is not a valid option
The values are being populated correctly. I can see them in the browser by inspecting the element. Yet if I select ID 1 for example it says "1 is not a valid option" etc for each Area object. It gets stuck at validation, doesn't even go to the action. I've done similar things in other parts of the site/other sites and they work fine.
Why is this validation incorrectly blocking the form submission and how do we fix this?
Seems like you just need to create an Array of your Map object.
if ($val =='yes') {
$areas = Area::get()->filter('ParentID', '0');
return $areas->map('ID', 'Name')->toArray();
}
Normally you could just use the Map object as the source for a DropdownField. But I think the DependentDropdownField has a little trouble with the Map object.
I do not know how to set a callback function for the view record page in codeigniter.
I use the callback_column function and it does what I need in the grid view, but on the view record page it does not work.
I searched their site and forum and did not found anything that could help me.
My code looks like:
$zeus = new grocery_CRUD();
$zeus->set_theme('bootstrap');
// $zeus->set_language('romanian');
$zeus->set_table('programari');
$zeus->columns(array('id_client', 'id_sala', 'denumire', 'numar_persoane', 'observatii'));
$zeus->callback_column('id_sala',array($this,'_test_function'));
$cod = $zeus->render();
$this->_afiseaza_panou($cod);
public function _test_function($row, $value)
{
return '0';
}
write this lines in \libraries\Grocery_CRUD.php
at line number 3530
protected $callback_read_field = array();
than put this function after constructor call
public function callback_read_field($field, $callback = null)
{
$this->callback_read_field[$field] = $callback;
return $this;
}
//Now update this function to manage the field outputs using callbacks if they are defined for the same
protected function get_read_input_fields($field_values = null)
{
$read_fields = $this->get_read_fields();
$this->field_types = null;
$this->required_fields = null;
$read_inputs = array();
foreach ($read_fields as $field) {
if (!empty($this->change_field_type)
&& isset($this->change_field_type[$field->field_name])
&& $this->change_field_type[$field->field_name]->type == 'hidden') {
continue;
}
$this->field_type($field->field_name, 'readonly');
}
$fields = $this->get_read_fields();
$types = $this->get_field_types();
$input_fields = array();
foreach($fields as $field_num => $field)
{
$field_info = $types[$field->field_name];
if(isset($field_info->db_type) && ($field_info->db_type == 'tinyint' || ($field_info->db_type == 'int' && $field_info->db_max_length == 1))) {
$field_value = $this->get_true_false_readonly_input($field_info, $field_values->{$field->field_name});
} else {
$field_value = !empty($field_values) && isset($field_values->{$field->field_name}) ? $field_values->{$field->field_name} : null;
}
if(!isset($this->callback_read_field[$field->field_name]))
{
$field_input = $this->get_field_input($field_info, $field_value);
}
else
{
$primary_key = $this->getStateInfo()->primary_key;
$field_input = $field_info;
$field_input->input = call_user_func($this->callback_read_field[$field->field_name], $field_value, $primary_key, $field_info, $field_values);
}
switch ($field_info->crud_type) {
case 'invisible':
unset($this->read_fields[$field_num]);
unset($fields[$field_num]);
continue;
break;
case 'hidden':
$this->read_hidden_fields[] = $field_input;
unset($this->read_fields[$field_num]);
unset($fields[$field_num]);
continue;
break;
}
$input_fields[$field->field_name] = $field_input;
}
return $input_fields;
}
than call same as other callback functions
As far as I'm aware GroceryCRUD doesn't provide callbacks or another means of overriding the default output in the view state.
The solution to customising this would be to create a custom view to which you will insert the data from your record. This way you can customise the layout and other presentation.
What you would then do is unset the default read view with:
$crud->unset_read();
And add a new action where there are details on how to do this here.
What to do with the new action is point it to a URL that you map in routes.php if necessary and handle it with a new function in your controller. You'll either have to write a model function to retrieve the data since this isn't passed from GC or you can use the action to target a callback and feed $row to it via POST or something so that the data for the record is accessible in the view. (Look at the example in the link above).
I'm creating a new widget that allows custom text/HTML to be added to the page. I noticed that if you enter text containing double-quotes, the first occurrence of it and everything after gets cut off, so that you're missing data when you try to edit the widget again.
To make sure I didn't screw something up, I was able to verify this issue on a fresh install of Magento (1.7), by adding a stock widget -- Catalog Product Link -- to the page. If you set the Anchor Custom Text to something with double-quotes, insert, and edit again, you will see the text has been truncated.
I'm not sure where the problem occurs. The data is successfully written to the tinyMCE content element, but somehow gets malformed between there and an Ajax.Request call for the admin/widget/loadOptions route.
I found a related article here:
http://www.behrendt.io/2013/04/12/using-a-wysiwyg-editor-in-a-magento-widget/
The author mentions at the bottom a need for overriding a controller to use base64 encoding when transmitting data for widgets. This seems like it might work for me, but I wanted to be sure.
Here's a visual example of the problem I'm experiencing:
Anyone seen this before? Know where it comes from? How to fix? :) Thanks.
Looks like that article put me in the right direction, by overriding Mage_Widget_Adminhtml_WidgetController:
http://www.behrendt.io/2013/04/12/using-a-wysiwyg-editor-in-a-magento-widget/
I took his solution a step further and decided to encode ALL values when building the widget code:
# Namespace_Module_Adminhtml_WidgetController extends Mage_Widget_Adminhtml_WidgetController
public function buildWidgetAction()
{
$type = $this->getRequest()->getPost('widget_type');
$params = $this->getRequest()->getPost('parameters', array());
$asIs = $this->getRequest()->getPost('as_is');
if($type == 'namespace/module_widget')
{
foreach($params as $key => $value)
{
$params[$key] = base64_encode($value);
}
}
$html = Mage::getSingleton('widget/widget')->getWidgetDeclaration($type, $params, $asIs);
$this->getResponse()
->setBody($html);
}
This meant I also had to decode them when loading the widget for editing:
# Namespace_Module_Adminhtml_WidgetController extends Mage_Widget_Adminhtml_WidgetController
public function loadOptionsAction()
{
try {
$this->loadLayout('empty');
if( ($paramsJson = $this->getRequest()->getParam('widget')) )
{
$request = Mage::helper('core')->jsonDecode($paramsJson);
if(is_array($request))
{
$optionsBlock = $this->getLayout()->getBlock('wysiwyg_widget.options');
if(isset($request['widget_type']))
{
$optionsBlock->setWidgetType($request['widget_type']);
}
if(isset($request['values']))
{
if($optionsBlock->getWidgetType() == 'namespace/module_widget')
{
foreach($request['values'] as $key => $value)
{
$request['values'][$key] = base64_decode($value);
}
}
$optionsBlock->setWidgetValues($request['values']);
}
}
$this->renderLayout();
}
}
catch (Mage_Core_Exception $e)
{
$result = array('error' => true, 'message' => $e->getMessage());
$this->getResponse()
->setBody(Mage::helper('core')->jsonEncode($result));
}
}
And finally, in my widget block, I had to decode all data on-the-fly:
# Namespace_Module_Block_Widget
public function getData($key = '', $index = null)
{
if('' === $key)
{
$data = $this->_data;
foreach($data as $key => $value)
{
if(is_scalar($value))
{
$data[$key] = base64_decode($value);
}
}
}
else
{
$data = parent::getData($key, $value);
if(is_scalar($data))
{
$data = base64_decode($data);
}
}
return $data;
}
It would be nice if a similar encoding mechanism was part of core code.
I have an autocomplete field in my form that lets me select a contact name and associate it with a listing.
On the edit page however, it will display the contact ID and not his firstname and lastname.
How can I do that?
This is getEdit() method in ListingsController.php
public function getEdit($id)
{
// get the nerd
$listings = Listing::find($id);
$listings->images = Image::where('listing_id', $id)->get();
$ImagesFileName = "";
$listings->imagesFiles = "";
if ($listings->images) {
foreach ($listings->images as $CurImage) {
$ImagesFileName[] = $CurImage['image_name'];
}
$listings->imagesFiles = implode(",", $ImagesFileName) ;
}
$query = DB::table('contacts')->get();
foreach ($query as $firstname)
{
$name[] = $firstname->firstname." ".$firstname->lastname;
}
$fullname = json_encode($name);
// show the edit form and pass the nerd
$this->layout->content = View::make('listings/edit')
->with('listings', $listings)
->with('getClientsByLetters', $fullname);
}
This is action_getClientsByLetters() in ListingsController.php
public function action_getClientsByLetters() {
$term = Input::get('query');
$data = array();
$query = DB::query("
SELECT * FROM contacts
WHERE MATCH (contact)
AGAINST('+".$term."*' IN BOOLEAN MODE)
");
foreach ($query as $results => $contact) {
$data[] = array(
'id' => $contact->id,
'value' => $contact->firstname
);
}
return json_encode($data);
}
This is the field in views/listings/edit.blade.php
<p>{{ Form::text('contact_id', null, array('class'=>'form-control', 'placeholder'=>'Contact Name', 'id'=>'contact', 'onblur'=>'test()')) }}</p>
And this is the javascript code on the same view
$(function() {
var availableTags = <?php echo $getClientsByLetters; ?>;
$( "#contact" ).autocomplete({
source: availableTags,
minLength: 2,
});
});
I think this is just a problem of approach. Normally I would just set up this kind of field as a SELECT control, using the ID as the key field and the name as the displayed option values. In all modern browsers you get progressive autocomplete on SELECT controls by default. One important question, however, is whether you are intending users to be able to add arbitrary new names to this field, or if it is intended to only allow existing names with assigned IDs to be entered. In the latter case then yes, we probably need to figure out how to get your autocomplete text field to work instead.
Your action_getClientsByLetters looks close, but I don't think it's ever being run.
Autocomplete is accepting an array of strings or an array of objects where each object has both a label and a value, which I think is what you should be aiming for.
With that in mind, I'd add a function whose sole responsibility is to fetch the contacts and put them in the required form that autocomplete is expecting, which you would probably be using the array of objects because it looks like your going to need the ID later.
function getContacts()
{
$contacts = Contact::all();
$autoComplete = array();
foreach($contacts as $contact) {
$autoComplete[]['label'] = $contact->first_name . ' ' . $contact->last_name;
$autoComplete[]['value'] = $contact->id;
}
return json_encode($autoComplete);
}
Then you'd call the view with...
$this->layout->content = View::make('listings/edit')
->with('listings', $listings)
->with('getClientsByLetters', $this->getContacts());
I'm wondering what would be best to do. Right now I have running a query to see if I have any results returned and if none are returned I return NULL.
On my controller I send that resultset whether it be an object or NULL to my table and it echos the rows on the view page.
For my tables I am using the jquery datatables plugin. I'm trying to figure out how I can have it handle the data when the sent value is NULL that way it doesn't show me an error when it hits my foreach loop.
Controller:
$news_articles = $this->news_model->get_news_articles();
$this->data['news_articles'] = $news_articles;
Model:
/**
* Get news articles
*
* #return object
*/
function get_news_articles()
{
$query = $this->db->get($this->news_articles_table);
if ($query->num_rows() > 0) return $query->result();
return NULL;
}
View:
$tmpl = array ( 'table_open' => '<table class="table" id="newsarticles-table">' );
$data = array('name' => 'newsarticles', 'class' => 'selectall');
$this->table->set_heading(form_checkbox($data), 'ID', 'Date', 'Title');
$this->table->set_template($tmpl);
foreach ($news_articles as $row)
{
$checkbox_data = array(
'name' => 'newsarticles',
'id' => $row->id
);
$this->table->add_row(form_checkbox($checkbox_data), $row->id, $row->date_posted, $row->article_title);
}
echo $this->table->generate();
I typically respond using JSON and then add a "success" type boolean and then check that value before trying to process any data. It also allows for an easy way to place an error message in the response if something goes wrong.
Just another idea
Model
function get_news_articles()
{
$query = $this->db->get($this->news_articles_table);
if ($query->num_rows() > 0) return $query->result();
return FALSE;
}
Controller
$news_articles = $this->news_model->get_news_articles();
if(!$news_articles) $this->data['success'] = FALSE;
else
{
$this->data['news_articles'] = $news_articles;
$this->data['success'] = TRUE;
}
In the view
if($success)
{
foreach ($news_articles as $row)
{
//....
}
}
else echo "No results found !";
Just return an empty array from the model, if there are not results. That way your foreach won't break. It just won't loop over anything.