Im having some trouble getting on with my first codeigniter project, and i have a feeling the answer is really near...
Im populating a form of inputfields from a database, and when the user submits the form, it should run through the rows of the database, and update the content based on the 'id' of each row.
My form (in the view 'admin.php') looks something like this:
<?php echo form_open('admin/update'); ?>
<?php foreach($content as $row) : ?>
<?php echo form_input('id['. $row->id .'][id]', $row->id); ?>
<?php echo form_input('id['. $row->id .'][order]', $row->order); ?>
<?php echo form_input('id['. $row->id .'][title_text]', $row->title_text); ?>
<?php echo form_textarea('id['. $row->id .'][body_text]', $row->body_text); ?>
<?php
if ($row->visibility == 'visible') {
echo form_checkbox('id['. $row->id .'][visibility]', 'visibility', TRUE);
} else {
echo form_checkbox('id['. $row->id .'][visibility]', 'visibility', FALSE);
}
?>
<?php endforeach;?>
<?php echo form_submit('Save Changes', 'Save Changes'); ?>
<?php echo form_close(); ?>
Now, most of this is based on a mix of tutorials and help documents. Here's the code from the controller 'admin', that i call when people click 'Save changes':
function update()
{
$this->load->model('admin_model');
$this->admin_model->updateRecords($_POST['id']);
$this->index();
}
The data is then passed on to the model, wherein the function looks like this:
function updateRecords($data)
{
$this->db->insert_batch('content', $data);
}
This is the only way i have gotten it to somehow work. It inserts the data fine, but adds it in new rows instead of updating the ones already there, according to their unique 'id's.
For good orders sake, here's the columns i have in my mySQL database:
id (PRIMARY and AUTOINCREMENT)
order
title_text
body_text
visibility
origin_date
Thanks alot in advance :)
Here's how I might do it, I added in some basic error handling; Hope this explains itself:
Model
function updateRecords($records)
{
// Method 1
// If you want to proceed if there is an error
$errors = array();
// Method 2
// If you want to rollback if there is an error
$this->db->trans_start();
foreach ($records as $id => $values):
// Your form fields conveniently match the column names
// No need to create the data array, we already have it
// If you want to update `origin_date` just do this:
// $values['origin_date'] = time();
// See if checkbox values were sent
// Assumes visibility is integer
if (empty($values['visibility']))
{
$values['visibility'] = 0;
}
else
{
$values['visibility'] = 1;
}
// Using method 1, store the error id in an array
$this->db
->where('id', $id)
->update('content', $values) OR $errors[] = $id;
// If using method 2, stop the loop
$this->db
->where('id', $id)
->update('content', $values) OR break;
endforeach;
// Method 1
// Return the array of failed updates
// Controller will check if the array is empty
// You can tell the user which updates failed or just how many
return $errors;
// Method 2
// This will return TRUE or FALSE
$this->db->trans_complete();
return $this->db->trans_status();
}
Controller
function update()
{
$records = $this->input->post('id');
$this->load->model('admin_model');
// Make sure to return errors here if they occur
// Using method 1
$errors = $this->admin_model->updateRecords($records);
$num_errors = count($errors);
if ($num_errors > 0)
{
echo "There were {$num_errors} errors!";
// You can optionally print which records had errors
echo "There were errors updating these rows: ".implode(', ', $errors);
}
// Using method 2
$success = $this->admin_model->updateRecords($records);
if ( ! $success)
{
echo "Error performing update, no records were saved.";
}
$this->index();
}
Don't just echo the errors of course, use some kind of message library, session data, or send variables to the view. Some references:
http://codeigniter.com/user_guide/database/transactions.html
http://codeigniter.com/user_guide/database/active_record.html#update
Related
I have following data array returned by Item_model. This array included some values of MySQL tables columns such as 'r_qty' and 'ap_qty'.
$this->data['issueData']=$this->Item_model->ItemRequestfromHDData($id);
Item_model
function ItemRequestfromHDData($id)
{
$this->db->select('store_update_stock.*,store_update_stock_details.*,tbl_user.username,store_item.*,
sum(qty) as avqty, sum(store_update_stock_details.r_qty) as r_qty,
sum(store_update_stock_details.ap_qty) as ap_qty');
$this->db->from('store_update_stock_details');
$this->db->join('store_update_stock', 'store_update_stock.update_stock_id=store_update_stock_details.update_stock_id');
$this->db->join('tbl_user', 'store_update_stock.supplier=tbl_user.userId');
$this->db->join('store_item', 'store_update_stock_details.item=store_item.item_id', 'left');
$this->db->where(array('store_update_stock.update_stock_id' => $id, 'store_update_stock_details.status' => 1));
$this->db->group_by('store_item.item_id');
$q = $this->db->get();
if ($q->num_rows() > 0) {
return $q->result();
}
return false;
}
I want to assign these two columns / values to variables. I tried following assignments.
$r_qty = $data['r_qty'];
$ap_qty = $data['ap_qty'];
but didn't get the expected result. What may be going wrong ? Can anyone help ?
As per codeigniter documentation,
result()
This method returns the query result as an array of objects, or an
empty array on failure.
Typically you’ll use this in a foreach loop, like this:
$query = $this->db->query("YOUR Q enter code here QUERY");
foreach ($query->result() as $row)
{
echo $row->title;
echo $row->name;
echo $row->body;
}
So, your code should be
foreach ($this->data['issueData'] as $data)
{
$r_qty = $data->r_qty;
$ap_qty = $data->ap_qty;
}
How do you check within the view template if the result object contains any entries?
(There was a similar question already, but this one is slightly different)
Take the CakePHP 3 blog tutorial for example. They show how to list all articles on one page:
// src/Controller/ArticlesController.php
public function index() {
$this->set('articles', $this->Articles->find('all'));
}
And the view template:
<!-- File: src/Template/Articles/index.ctp -->
<table>
<tr>
<th>Id</th>
<th>Title</th>
</tr>
<?php foreach ($articles as $article): ?>
<tr>
<td><?= $article->id ?></td>
<td>
<?= $this->Html->link($article->title, ['action' => 'view', $article->id]) ?>
</td>
</tr>
<?php endforeach; ?>
</table>
Disadvantage: if there are no entries in the database the HTML table is still rendered.
How can I prevent this and show a simple message like "Sorry no results" insteat?
In CakePHP 2 I used
if ( !empty($articles['0']['id']) ) {
// result table and foreach here
} else {
echo '<p>Sorry no results...</p>';
}
But since $articles is now an object this doesn't work anymore... Is there a new "short way" to check the result object? Or do you usally use another foreach first, like
$there_are_results = false;
foreach ($articles as $article) {
if ( !empty($article->id) ) {
$there_are_results = true;
break;
}
}
if ( $there_are_results == true ) {
// result table and second foreach here
} else {
echo '<p>Sorry no results...</p>';
}
Thanks for your hints.
You can use the iterator_count() function to know if there are results in the set:
if (iterator_count($articles)) {
....
}
You can also use the collection methods to get the first element:
if (collection($articles)->first()) {
}
Edit:
Since CakePHP 3.0.5 the best way to check for emptiness on a query or a result set is this:
if (!$articles->isEmpty()) {
...
}
I believe you can call $articles->count() from your template. (Check for 0)
Something I have been struggling for a while..
if(!$articles->isEmpty()) {
gives error on empty value
Call to a member function isEmpty() on null
<?php if(iterator_count($articles)) { ?>
Argument 1 passed to iterator_count() must implement interface Traversable, null given
<?php if (collection($articles)->first()) {?>
Only an array or \Traversable is allowed for Collection
I got
to work, the problem if you render a different view in the controller $this->render('index');
for a function you should do that after the values has been set
I am trying to create a form "Movie" as follows and my database accepts movie_id, title, year, and description. When I run the code it tries to store the "array" as the year input. This is the error I get => movie_id, title, year, description) VALUES (NULL, 'Movie1', Array, 'some text')
THE VIEW:
<?php
echo $this->Form->create('Movie'); ?>
<?php echo __('Add Movie'); ?>
<?php
echo $this->Form->hidden('movie_id');
echo $this->Form->input('title');
echo $this->Form->input('year', array(
'type'=>'date',
'dateFormat'=>'Y',
'minYear'=>'1990',
'maxYear'=>date('Y'),
));
echo $this->Form->input('description');
?>
<?php echo $this->Form->end(__('Submit'));
?>
THE CONTROLLER:
public function add() {
if ($this->request->is('post')) {
$this->Movie->create();
if ($this->Movie->save($this->request->data)) {
$this->Session->setFlash(__('The movie has been created'));
$this->redirect (array('action'=>'index'));
}
else {
$this->Session->setFlash(__('The movie could not be created. Please, try again.'));
}
}
}
I believe your problem is here:
$this->Form->input('year', array(
'type'=>'date',
'dateFormat'=>'Y',
'minYear'=>'1990',
'maxYear'=>date('Y'),
));
Instead of it you should use
$this->Form->input('Movie.year', array(
'type'=>'text',
'name' => 'data[Movie][year]',
));
If you don't want to use 'text' type input, then use 'select' with your options array. Here main concern is, Movie.year and overwrite the default CakePHP's naming procedure to 'name' => 'data[Movie][year]'. Check here.
The best thing to do is to debug your $this->request->data.
I'm not sure myself, but I once have this problem. What happen is, when you define an input as a date, it will be behave as an array.
It will become $this->request->data['Movie']['year']['year'];
You can modify the data by doing the following
$this->request->data['Movie']['year'] = $this->request->data['Movie']['year']['year'];
I've read through a couple posts which are really close to the same question I am about to ask, but I'm still not grasping the MVC way. One example was close and titled "Codeigniter db query on two tables", but I'm getting the "Undefined property" errors which reinforces that fact I'm not calling objects properly. I would prefer not to use DataMapper simply because if I need to go out and get additional classes, then I might just go back to the procedural coding route. I was just a little excited on understanding how this framework implementation works.
I have two tables, lets call one "apps" and the other would be "mappings". "Apps" are applications which use apache as a reverse proxy configuration like this:
ProxyPass /mapping/ https:///
ProxyPassReverse /mapping/ https:///
Some of the apps could request multiple reverse proxy mappings, so its a 1-many design.
My objective is to use a select statement for the apps table then print out every column I desire from that table:
for example, I currently have two application defined in my "apps" table, in which one of the applications has two reverse proxy mappings,and the other has one. The prxypass lines below the data from the apps table would be coming from the apps_mapping table.
App: App ABC
Owner: ABC Team
Date Requested: 2013-10-27
ProxyPass /ABC-Resource/ https://<to-some-internal-URL>/
ProxyPassReverse /ABC-resource/ https://<to-some-internal-URL>/
App: App XYZ
Owner: XYZ Team
Date Requested: 2013-10-27
ProxyPass /XYZ-mapping/ https://<to-some-internal-URL>/
ProxyPassReverse /XYZ-mapping/ https://<to-some-internal-URL2>/
ProxyPass /anothermapp/ https://<to-some-internal-URL>/
ProxyPassReverse /anothermapping/ https://<to-some-internal-URL2>/
I've tried to model my first attempt of using CodeIgniter by way of modifying their tutorial provided by the CodeIgniter example in their user_guide.
Now what I haveis.
Model Class:
<?php
class Prp_model extends CI_Model {
public function __construct()
{
$this->load->database();
}
//GET APP THE ROWs FROM THE apps TABLE
public function get_appsUsingPrp()
{
$query = $this->db->get('apps');
return $query->result_array();
}
//GET THE MAPPINGS FROM THE apps_mappings TABLE
//FOR EACH APP BY WAY OF UNIQUE id IN THE appsTABLE
public function get_prpMappings($id = FALSE)
{
$query = $this->db->get_where('apps_mappings', array('resourceID' => $id));
return $query->result_array();
}
?>
for the above class, instead of using a return function on get_prpMappings() should I be putting it in a variable? Even then, how would one supply a value for the $id in the code called int he views page?
Controller Class
<?php
class Prp extends CI_Controller {
public function __construct()
{
parent::__construct();
$this->load->model('prp_model');
}
public function index()
{
$data['apps'] = $this->prp_model->get_prp();
$data['mappings'] = $this->prp_model->get_prpMappings($id);
$data['title'] = 'PRP archive';
$this->load->view('templates/header', $data);
$this->load->view('prp/index', $data);
$this->load->view('templates/footer');
}
public function view($id)
{
$data['apps'] = $this->prp_model->get_prp();
$data['mappings'] = $this->prp_model->get_prpMappings($id);
}
}
View
<?php
print_r($apps);
print_r($mappings);
foreach ($apps as $details): ?>
<div id="main">
<ul>
<?php echo "<li>App Name: ".$details['app_name'] ?>
<?php echo "<li>EPRID: ".$details['EPRID'] ?>
<?php echo "<li>Requestor: ".$details['requestor'] ?>
<?php echo "<li>Team PDL: ".$details['PDL'] ?>
<?php foreach ($mappings as $map): ?>
<?php echo "<li>ProxyPass /".$map['resource']."/ ".$map['destURL'] ?>
<?php echo "<li>ProxyPassReverse /".$map['resource']."/ ".$map['destURL'] ?>
<?php endforeach ?>
</ul>
</div>
<hr />
<?php endforeach ?>
?>
Getting down to my confusion.... Should I be doing all the foreach looping in the model and/or the controller classes if I'm not going to setup the DataMapper Class?
Is there a way to call the get_prpMappings($id) function defined in the model class directly in the view? The only way for that function to work for me, is when the id of the app row is provided when iterating through the rows.
I tried putting $this->prp_model->get_prpMappings($id); in the view, but the view page doesn't seem to identify the class.
Lastly.. By the looks of it, it appears I would be doing more coding with a framework (such as CodeIgniter or Zend), then it would be to just write everything from scratch. But I'm under the impression the framework is to provide convenience down the road of additional coding. I'm just not seeing it. Please help.
UPDATED! I hope I understand this.
I have modified my logic to express the output desired. Would you be able to tell me if it is an inefficient way? Here is the logic:
MODEL CLASS:
<?php
class Prp_model extends CI_Model {
public function __construct()
{
$this->load->database();
}
//GET APP THE ROWs FROM THE sharedITG TABLE
public function get_appsUsingPrp()
{
$query = $this->db->get('sharedITG');
return $query->result_array();
}
//GET THE MAPPINGS FROM THE sharedITG_mappings TABLE
//FOR EACH APP BY WAY OF UNIQUE id IN THE shareITG TABLE
public function get_prpMappings($id = FALSE)
{
$this->db->select('resourceID, resource, destURL');
$query = $this->db->get_where('sharedITG_mappings', array('resourceID' => $id));
return $query->result_array();
}
public function get_prp()
{
//loop through all apps
foreach ($this->get_appsUsingPrp() as $app)
{
//print_r($app);
foreach ($this->get_prpMappings($app['id']) as $mappings)
{
$map[] = $mappings;
}
}
return $map;
}
}
Controller Class
<?php
class Prp extends CI_Controller {
public function __construct()
{
parent::__construct();
$this->load->model('prp_model');
}
public function index()
{
$data['mappings'] = $this->prp_model->get_prp();
$data['apps'] = $this->prp_model->get_appsUsingPrp();
$data['title'] = 'PRP archive';
$this->load->view('templates/header', $data);
$this->load->view('prp/index', $data);
$this->load->view('templates/footer');
}
public function view($id)
{
$data['apps'] = $this->prp_model->get_appsUsingPrp();
$data['mappings'] = $this->prp_model->get_prp();
}
}
VIEW code:
<?php
//print_r($apps);
//print_r($mappings);
foreach ($apps as $details): ?>
<div id="main">
<ul>
<?php echo "<li>App Name: ".$details['app_name'] ?>
<?php echo "<li>EPRID: ".$details['EPRID'] ?>
<?php echo "<li>Requestor: ".$details['requestor'] ?>
<?php echo "<li>Team PDL: ".$details['PDL'] ?>
<?php
foreach ($mappings as $map)
{
if ($details['id'] == $map['resourceID'])
{
echo "<li>ProxyPass /".$map['resource']."/ ".$map['destURL'];
echo "<li>ProxyPassReverse /".$map['resource']."/ ".$map['destURL'];
}
}
?>
</ul>
</div>
<hr />
<?php endforeach ?>
So after further research, I found a forum that gave me a pretty decent example. I'm not sure if its the most optimal way to do this, but it worked for me and still (kind of) maintained the MVC model for Code igniter. The foreach in the function below is what I added. It appears it loops through the current result and obtains all rows for the second table that has a relationship between Table2ID and IDFromTable. Then adds the results by way of $result{$key}.
public function get_resuts()
{
$query = $this->db->get('table1');
$result = $query->result_array();
foreach ( $result as $key => $row )
{
$query = $this->db->get_where('Table2', array('Table2ID' => $row['IDFromTable']));
$row['DesiredFieldFromTable#'] = $query->result_array();
$result[$key] = $row;
}
return $result;
}
Then its available in the view by looping through the second array with the content from the foreach results in the get_resuts() function. I'm interested in learning of other ways to do this too.
I'd like to create the title of my custom type posts based on the custom fields the user enters. For example i have fields 'first name' and 'last name' and want the title created from whatever the user enters and the permalink to create the slug from it too.
I've tried this at the end of my declaring the custom type in functions.php:
function save_member(){
global $post;
update_post_meta($post->ID, "fname", $_POST["fname"]);
update_post_meta($post->ID, "lname", $_POST["lname"]);
$this_post = array();
$this_post['ID'] = $post->ID;
$this_post['post_title'] = $_POST["fname"].' '.$_POST["lname"];
wp_update_post($this_post);
}
It just hangs if i add the last 4 lines of the code above.
Chances are that you are initiating a loop when wp_update_post is called by firing some do_action() call within there (or related function calls).
Do this:
public static function repair_title( $title ){
global $post;
// only do this for a specific type, may want to
// expand this as needed
if( "your_type" != $post->post_type ) return $title;
$firstname = $_POST["firstname"];
$lastname = $_POST["firstname"];
$title = $firstname . " " . $lastname;
return $title;
}
public static function repair_name( $name ){
global $post;
if( "your_type" != $post->post_type ) return $name;
$firstname = $_POST["firstname"];
$lastname = $_POST["firstname"];
$name = wp_unique_post_slug(
$firstname . " " . $lastname,
$post->ID,
$post->post_type,
0
);
return $name;
}
Edit: changed this upon noting a bit of a bug in my own code (I just happened to be working on this at the moment). I was using get_post_custom() and this is fired before the post object is changed, so you need to grab your values form the $_POST posted form variables.
Cheers!