Hey, im having this issue with cakephp, bascially i have a Has And Belongs To Many (HABTM) model relationship.
My models are Categroy and Project
bring all project data is fine, it comes out as [0]['Project'], [1]['Project'] ...etc
but when i use the relationship and pull out projects with certain categories in the categories controller i get these tpye of results [0] (all project data in [0] instead of [0]['Project']), [1] (project data and related model info) this is really messing my code up as i use one element view file to render my projects is there any way to return [0]['Project'] for both project controller and categories controller? thanks Chris
Hi sorry if my example isnt clear
i have projects and categories
when i pull a list of projects from the projects controller from my project model the results i get are in this format
[0]['Project'] = array(data...);
[1]['Project'] = array(data...);
[2]['Project'] = array(data...);
this is how the data is pulled and thats fine for me but when i pull projects per cetegory page using the HABTM relationship in the categories controller from the category model this is how my data is returned
['Project'][0] = array(data...);
['Project'][1] = array(data...);
['Project'][2] = array(data...);
which as you can see is a bit of a strain as i want to keep 1 element view file to display my projects, so far my view file prints data like so
<?php print $project['Project']['title']; ?> //data is returned [x]['Project']
<?php print $project['Feature']['title']; ?>
with the way the HABTM relationship is returning data i would need to do this
<?php print $project['title']; ?> //because data is returned ['Project'][x]
<?php print $project['Feature']['title']; ?>
can anyone help with this? thanks
This has frustrated me too. I like to have one set of elements that can be used for rendering both "primary" find results as well as related find results.
This is the way I currently deal with the differences in formats of results.
When calling find on, say, a "Project" model and wanting to render the related "Task" list, I run the "Task" key of the results through a function on its way into the element like so:
echo $this->element('tasks/index',array(
'data'=>make_primary('Task',$data['Task'])
));
My 'make_primary' function is like so:
function make_primary($alias,$data) {
$d = array();
foreach($data as $item) {
$related = array();
foreach($item as $key => $val) {
if(!is_numeric($key) && is_array($val)) {
$related[$key] = $val;
unset($item[$key]);
}
}
$d[] = array_merge(array($alias=>$item), $related);
}
return $d;
}
This returns a new array as though it was the result of a "primary" find query.
Related
I have 2 models, store and dvd with many to many relationship
dvd model:
public function stores() {
return $this->belongsToMany('App\store');
}
store model:
public function dvds ( ) {
return $this->belongsToMany('App\dvd');
}
Then, in controller, when I need fetch data from both models, I use code like this:
$res_store = store::orderBy("id","desc")->get();
foreach( $res_store as $row ) {
$dvds = $row->dvds()->get();
foreach ($dvds as $dvd) {
// echo columns here
}
}
This works, but my question is, I'm doing this in correct way? I'm asking because it seems 2 loops for this simple relation is somehow inefficient.
No, this is not the right way.
When looping over a $store, you can access the associated $dvd records via $store->dvds; there is no need to call $row->dvds()->get(), as that is executing a new query with the same result of $store->dvds. Full code should simply be:
$stores = Store::with("dvds")->orderBy("id", "DESC")->get();
foreach($stores AS $store){
foreach($store->dvds AS $dvd){
... // Do something with `$dvd`
}
}
The ::with("dvds") clause is known as "Eager loading", and prevents $store->dvds from needing execute another query behind the scenes.
Also, please name your models correctly. Classes in PHP are StudlyCase, so Store and DVD, not store and dvd.
I am trying to get some relational database handling to work, using PHP and MySQL. To combine output from two different tables, I found some help here on stackoverflow. On of the suggestions was to combine the output in one result object (as I understand it), so the code looks like this:
// Function to read all projects from database
public function get_projects() {
$query = "SELECT pt.*, at.* FROM ed_projects as pt, ed_project_address as at WHERE pt.project_id = at.project_id";
$db_result = $this->db->query($query);
$result_object = $db_result->result();
return $result_object;
}
Where ed_project is the name of the main table, and ed_project_address is the subtable (or whatever it is called), which contains streetname, postal code, city, etc. for each project. The address properties are linked to projects by a project_id.
I can get this to work on the "reading from the database" part, but when I try to use it, I get an error: "Fatal error: Cannot use object of type stdClass as array in (...)". As I am using CodeIgniter, it is passed through 2 additional steps:
Project View Controller:
/* index() - Project view controller
* Handles the showing of the project main page, that is the list of all
* projects found in the database. See also views/projects/index.php.
*/
public function index() {
$data['projects'] = $this->project_model->get_projects();
$data['title'] = 'Ejendomme';
$this->load->view('templates/header', $data);
$this->load->view('projects/index', $data);
$this->load->view('templates/footer');
}
Project View:
<?php foreach ($projects as $projects_item): ?>
<h2><?php echo $projects_item['pt.projects']->project_name ?></h2>
<div class="main"><?php echo $projects_item['at.address_street'] ?></div>
<?php endforeach ?>
The error is in Project View (index.php) on line 3. I have been reading quite a bit on objects in PHP, but the addition of the CodeIgniter framework seems to obscure things just enough for me to not get it :-(. Am I way off here? Or am I just missing the very last bit?
you just made mistake
at your model you returns the object
return $result_object;
but you using it as array at view.
you should use it like this
$projects_item->project_name
If you want to use it as array at your view you should return data from model like this
$result_object = $db_result->result_array();
Also your query will produce error if your both table has same column name. In that case you have to specify which column form which table you want to select.
First code:
$result=ResPlaces::model()->findall($criteria);
/*foreach($result as $value)
{
$model_name=ResRegistration::model()->findByAttributes(array('id'=>$value->user_id));
$model_image=ResImages::model()->findByAttributes(array('place_id'=>$value->id),array('limit'=>'1'));
}*/
}
echo CJSON::encode($result);
?>
i need to add model_name->company_name & $model_image->image
to my echo json array
Try to load the relations within the findAll so you don't need the foreach.
ResPlaces::model()->with('ResRegistration', 'ResImages')->together()->findAll($criteria);
For ResRegistration and ResImages use the relation names as defined in your model.
If you don't need all fields of these relations, you can specify a select in your $criteria.
See the guide for more info.
edit: I am not quite sure, why you cannot use relation. However you will need a loop, like you already have. Here is how you do it without relations:
First add the fields you want to use to your model. In this case
class ResPlaces extends CActiveRecord
{
public $name; // check that these do not collide with your models db fields
public $image;
[...]
}
In your controller do the loop like you did before:
foreach($result as $key => $value)
{
$result[$key]->name = ResRegistration::model()->findByAttributes(array('id' => $value->user_id));
// findByAttributes returns only one record, so you don't need the limit here
// if you want multiple records, you have to use findAllByAttributes
$result[$key]->image = ResImages::model()->findByAttributes(array('place_id' => $value->id), array('limit' => '1'));
}
That should do it. However I wouldn't recommend this way, because you have lots of additional database requests. If your $result is populated with say 100 records, you have in sum 200 additional queries which are not nessassary with a relation.
Note also that if you need these two other fields more often, it may be better to put these two queries which are now in the controller in your model. The afterFind() would be the right place.
In one of my controllers to fetch all the data for my general view page I use a foreach loop and then $object->column_name but now I have decided I would like to do a couple of things with this data:
Edit it -> It is an edit page for each $object by its $id
Use the $object->name field via the controller to enable me to use it in a $data['pageTitle']= Edit '.$object->name.';
What would be the best way to change the model below so that I can use it for many purposes / different ways of displaying the data for manipulation?
public function showAll()
{
$database = $this->db->get('form');
if($database->num_rows() > 0)
{
$row = $database->result();
}
return $row;
}
It is good practice to keep the database query part in your model rather than the controller.
In your controller you can do something like:
$recs = $this->sample_model->model_function();
foreach ($recs as $r)
{
$r->additional_info_appended_to_each_row = 'whatever';
}
This way you can append an additional variable to each database row for displaying / editing etc.
That code doesn't really show us enough of the relevant code to be able to answer that question. Assuming $database->result() returns an array of rows (a database query's result set) then the data it contains depends on how the query looks. All you show is is $this->db->get('form'), which can mean just about anything.
A generic answer to your question would be: Alter the SQL query to include the id and name fields. Then inject those into your view through your controller. (Or get them directly through the view. That's up to you.)
Not knowing a thing about your controllers or your views, here is an example that assumes your views extend the Smarty template engine.
public function GeneralController
{
public function defaultAction()
{
$model = new GeneralModel();
$objects = $model->getAll();
$view = new GeneralView();
$view->assign("objects", $objects);
$view->show();
}
}
The Smarty template would then use those rows when generating the HTML
{foreach $objects as $object}
<section class="object">
<header>
<h1>{$object->name}</h1>
</header>
<p>{$object->contents}</p>
<footer>
Edit
</footer>
</section>
{/foreach}
I am using codeIgniter. I need to create a multidimensional array of the data from the DB nammed college. The database has 3 columns: id, OfID and name. The OfID column contains the ID of the parent of that college. For colleges who don't have any parent have OfID as 0.
The array should contain the name, ID and OfID of colleges who have OfID=0 as the elements of first dimension. For colleges which have OfID!=0 should be put as 2nd (and so on) dimension array for the college whose ID they have as OfID.
I thought to do this recursively , but I am unable to finish this. I know there are a lot of mistakes in this please help.
The model class follows: (the controller calls meth() function)
class Model extends CI_Model
{
var $return_this=array();
function meth()
{
$loop_id=0;
getit($loop_id);
var_dump($return_this);
}
function getit($loop_id)
{
$index=0;
$query = $this->db->query("select * from college where OfID=$loop_id ORDER BY `OfID` ASC;");
if ($query->num_rows() > 0)
{
foreach ($query->result() as $row)
{
$pass=$row->id;
$temp=getit($pass);
if($temp==0)
$return_this[$loop_id]= $query->result();
}
}
else return 0;
}
}
Try something like this:
$rows = array();
foreach ($query->result() as $row)
{
$pass=$row->id;
$rows[] = getit($pass);
}
return $rows;
In any case, the best way I found myself to do recursion, is to take some recursive function that is really simple and you know every aspect about it, and to build it up step by step. By "step by step" I mean at first you don't pass all the values, but print them out, just to see what, where and how you get, then try to pass them. Recursive functions such as this one are brain damaging if you don't understand how it works.
The idea behind your one, is to get an array every time you stumble upon a college that has child colleges. I can see you get it right, just try to make this one as I told earlier - step by step and you will get a hang of it.