CodeIgniter Forums
DataMapper 1.6.0 - Printable Version

+- CodeIgniter Forums (https://forum.codeigniter.com)
+-- Forum: Archived Discussions (https://forum.codeigniter.com/forumdisplay.php?fid=20)
+--- Forum: Archived Libraries & Helpers (https://forum.codeigniter.com/forumdisplay.php?fid=22)
+--- Thread: DataMapper 1.6.0 (/showthread.php?tid=11358)



DataMapper 1.6.0 - El Forum - 05-04-2009

[eluser]camporter1[/eluser]
@OverZealous,

Sorry, I keep making dumb mistakes. Smile Not sure how that part got removed from my Model. Anyway, thanks for the constant, great help!


DataMapper 1.6.0 - El Forum - 05-05-2009

[eluser]defeed[/eluser]
hi, i'm not sure whether i have to post this question to DM forum or CI, but i hope someone could help me out.
I have a movie catalog application, which uses DataMapper. To make it simple, i'll give just a part of my db tables and models

movie model:
Code:
var $has_many = array('genre');

genre model:
Code:
var $has_many = array('movie');

movies table:
Code:
id: int
title: varchar
desc: text

genres table:
Code:
id: int
name: varchar

genres_movies table (join):
Code:
id: int
movie_id: int
genre_id: int

here's the movie_add() function in the movies controller
Code:
function movie_add() {
    $genres = $_POST['genres']['value'];
    
    $movie = new Movie();
    
    $genre = new Genre();
    // here we create a Genre object and put a selected genres there
    $genre->where_in('name', $genres)->get();
    
    $movie->title = $_POST['title'];
    $movie->desc = $_POST['desc'];
    
    // add a new movie to db
    $movie->save();
    
    $movie = new Movie();
    // get the latest added movie id to relate with genres
    $movie->order_by('id', 'desc')->limit(1)->get();
    $movie->save($genre->all);
    
    redirect('/movies');
}

and here's the movie_add view
Code:
<?= form_open('/movies/movie_add') ?>
Title: <?= form_input('title') ?>
Genre:<br />
<select multiple size="5" name="genres[]">
&lt;? foreach($genre_list->all as $genre): ?&gt;
    <option value="&lt;?= $genre->id ?&gt;">&lt;?= $genre->name ?&gt;</option>
&lt;? endforeach ?&gt;
</select><br />
Description:<br />&lt;?= form_textarea('desc') ?&gt;<br />
&lt;?= form_submit('submit', 'Add movie!') ?&gt;
&lt;?= form_close() ?&gt;

I understand that in order to link movie with multiple selected genres, I need to get those selected genre IDs from <select> element. So, how would I do that, preferably without using JavaScript? Now, this whole thing works, it just links new movie with all genres existing in genres table.


DataMapper 1.6.0 - El Forum - 05-05-2009

[eluser]OverZealous[/eluser]
You have like 75% of it. For one, you are running the wrong query to look up your $genre.
Code:
$genre->where_in('id', $genres)->get();
//               ^^^^

For another, there is no need to re-look up your $movie. The $movie object is automatically populated with the new $id, so you can simply save the $genres directly:
Code:
// immediately following the above code
$movie->title = $this->input->post('title');
$movie->desc = $this->input->post('desc');

if($movie->save($genre->all)) {
    redirect('movies');
} else {
    // There was an error saving or validating, check $movies->error
}

If you don't like saving the $genres at the same time, then you can do it in two steps (rarely necessary, though):
Code:
$success = $movie->save();
$success = $success && $movie->save($genre);
if($success) {
...



DataMapper 1.6.0 - El Forum - 05-05-2009

[eluser]defeed[/eluser]
thanks for correction! but still, any ideas on how to populate selected ids to array?


DataMapper 1.6.0 - El Forum - 05-05-2009

[eluser]OverZealous[/eluser]
I don't have anything on hand that has a multiple select set up. I think it's just:
Code:
$genres = $this->input->post('genres');

You already had the hard part figured out, using genres[] as the field name.

If that doesn't work, you might need to use array_map('intval', $genres) to force the values to be integers.

Play around a bit, and I'm sure you can get it working!


DataMapper 1.6.0 - El Forum - 05-05-2009

[eluser]defeed[/eluser]
I figured it out! That was quite simple, actually Smile

Code:
$genres = $_POST['genres']['value'];
the ['value'] part is unnecessary, and the selected items are automatically populated to $genres array. Thanks a lot for helping.


DataMapper 1.6.0 - El Forum - 05-05-2009

[eluser]defeed[/eluser]
one more question, if you dont mind
currently I have separate objects for actors and directors, which is not quite correct, since one person can be both actor and director even in one movie.
so, not to engender entities, I think it's reasonable to make one instead of two: people. But then I need one more object 'role' and one more table 'people_movies' which will contain fields: id, movie_id, person_id and role_id, right?
movie - has many people
person - has many roles and movies
role - has many people (and movies, if i'm correct)

the question is, how would get actors and directors separated when showing movies (one or all)? is it even possible?

atm, it's done the standard way:
Code:
$data['movie_list'] = new Movie();
$data['movie_list']->get();

Code:
&lt;? foreach ($movie_list->all as $movie): ?&gt;
&lt;?php $movie->actor->get(); ?&gt;
&lt;?php $movie->director->get(); ?&gt;

&lt;? foreach($movie->director->all as $director): ?&gt;
  &lt;?= $director->name ?&gt;
&lt;? endforeach ?&gt;

&lt;? foreach($movie->actor->all as $actor): ?&gt;
  &lt;?= $actor->name ?&gt;
&lt;? endforeach ?&gt;
&lt;? endforeach ?&gt;



DataMapper 1.6.0 - El Forum - 05-05-2009

[eluser]OverZealous[/eluser]
I'm not going into great detail here, because this has been discussed a lot, but basically DataMapper doesn't handle multiple relationships to the same object very well. This is one of the reasons why I extended DataMapper (since stensi has not been able to work on it in a while).

Check out the link in my sig, for DMZ. DMZ is a drop-in replacement for DM, with no code changes unless you want the new features.

The basic process is to create one model (e.g. Person), and then you'll be able to relate that model to a movie multiple times, using what I call related_fields. Then you'll be able to do something like this:
Code:
class Movie extends DataMapper {
    // you might prefer this to be a $has_many, since
    // some movies have multiple directors.
    $has_one = array(
        'director' => array(
            'class' => 'person',
            'other_field' => 'directed_movie'
        )
    );
    $has_many = array(
        'actor' => array(
            'class' => 'person',
            'other_field' => 'acted_movie'
        )
    );
}

//----------------------------------------

class Person extends DataMapper {
    $has_many = array(
        'acted_movie' => array(
            'class' => 'movie',
            'other_field' => 'actor'
        ),
        'directed_movie' => array(
            'class' => 'movie',
            'other_field' => 'director'
        )
    );
}

//----------------------------------------

$movie->save_director($person1);
$movie->save_actor($person2->all);

//----------------------------------------

$movie->director->get();
$movie->actor->get();



DataMapper 1.6.0 - El Forum - 05-05-2009

[eluser]wolffc[/eluser]
One issue I am running into is that we have a mater database with replicants (I know it's not exactly ideal but I don't have a choice). I need to be able to switch connections when saving, reading, deleting, whatever. My default is the replicant. I tried doing
Code:
$this->db = $this->load->database('master',TRUE)

I also tried

Code:
$CI =& get_instance();
$CI->db = $this->load->database('master',TRUE);

They both change db for the controller but not each datamapper model. I would have thought that each model's db would reference the same one but it does not appear to. The only option I can get to work is by setting each model's db just before a save. But that seems like a waste of time. Is that my only option? Thanks for the help.


DataMapper 1.6.0 - El Forum - 05-05-2009

[eluser]OverZealous[/eluser]
The $db variable is set on each model after it is loaded. It uses $CI->db.

To make it work, you'll need to replace $CI->db before creating or looking up any models:
Code:
// possible helper function
function use_master_database() {
    $CI =& get_instance();
    $CI->db = $CI->load->database('master', TRUE);
}

function use_replicant_database() {
    $CI =& get_instance();
    $CI->db = $CI->load->database('replicant', TRUE);
}

Usage:
Code:
// in controller
use_master_database();
$thingy = new Thingy();
$thingy->get_by_id(1); // looks up in master

use_replicant_database();
$thingy2 = new Thingy();
$thingy2->get_by_id(1); // looks up in replicant

$thingy->name = "Hello";
$thingy->save(); // saves into MASTER database.