To query documents by ObjectID, you can’t just pass in a string, you need to send in PHP’s Object/Type MongoID
public function action_getstuff()
{
$mongodb= \Mongo_Db::instance('default');
$userID1 = '4f98c2f6f26c618f21000002';
$userID2 = '4f976a38f26c619121000002';
$result = $mongodb->where_in('_id', array($userID1, $userID2))->get('Users');
if($result)
{
echo "got Results with userID strings";
}
$result = null;
$mongoID1 = new MongoID($userID1);$mongoID2 = new MongoID($userID2);
$result = $mongodb->where_in('_id', array($mongoID1, $mongoID2))->get('Users');
if($result)
{
echo "got Results with MongoID objects";
}
}
You should get results on the 2nd query with the MongoID objects.
If you are in your Model, and using a namespace, remember to reference the MongoID object with \MongoID
For some reason this was a DOOZY of problem. And it’s because i didn’t thoroughly read MongoDB’s documentation!!!! They have it right there… The $ Positional operator is your friend! You should read mongodb’s documentation page. But since you’re already here, let’s take a look at a very simple example using FuelPHP. Let’s assume your schema looks like this. You are tracking anytime somebody clicks on a users links in their profile.
I like to query the data first, to make sure we have a row to update. I don’t use an upsert here, because the mongoDB documentation (yea, i read that in there this time) states that if it does not exist, it will insert a field name containing the ‘$’ sign.
//Somehwere in our controller we have a function like this
...
public static function click_link($url)
{
$mongodb = \Mongo_Db::instance('default');
$result = $mongodb->where(array('username'=>"jim", 'links.url'=>$url))->get_one('Links');
if($result)
{
$mongodb->where(array('username'=>"jim", 'links.url'=>$url]))->update('Links',
array('$inc'=> array('links.$.count'=> 1)),
array(),
true);
}
}
the first lines should be familiar to you. Choosing the instance, then finding a row in the DB. The thing that might seem strange is the “links.url” part. Well you can use the dot notation to dig deeper into embedded objects. So I could go as deep as i needed if I had to (comments.author.firstname…) if i had to go deeper.
the tricky noise, and what was causing me headache, was when you do an update, you can’t just use the dot notation. You have to throw a dollar sign in between, so i did ‘links.$.count’, then it knew in the scope of this document where to update the count. This was a basic instruction, if you don’t understand it read over the documentation at mongoDB. If it’s still eluding you feel free to message me, i’d be glad to help, as it caused me a night of frustration.
I finally found the mongodb google groups and it lead me to the solution. Great place, check it out if you haven’t
So if you want to use $inc with fuelphp and mongodb, or any of the other update commands besides $set ( which is the default) it is possible. It’s undocumented however unless you look into the code. Again here is our controller code
Class Controller_About extends Controller
{
public function action_index()
{
$data = array();
$data['name'] = "CopyPasteCoder";
$data['age'] = 27;
//echo "This is my first attempts at MVC architecture.";
return View::forge('about', $data);
}
public function action_water()
{
$result = Model\About::get_count();
echo "i'm thirsty $result";
}
public function action_supMe($name = '')
{
$data = array();
$data['name'] = $name;
$data['age'] = 27;
return View::forge('about', $data);
}
}
And our model looks like this
namespace Model;
class About extends \Model
{
public static function get_count()
{
$mongodb = \Mongo_Db::instance('default');
$result = $mongodb->where(array("name"=>'about'))->get('counter');
$count = $result[0]['count'];
return $count;
}
}
let’s modify the get_count() function to actually increment the count in 2 ways.
public static function get_count()
{
$mongodb = \Mongo_Db::instance('default');
//increment the old fashion way, get row then increment
$result = $mongodb->get('counter');
$count = $result[0]['count'] + 1;
$mongodb->where(array('name'=> 'about'))->update('counter',
array('count'=> $count)
)
;
//using increment functionality ($inc)
$mongodb->where(array('name'=>'about'))->update('counter', array('$inc' => array('count'=>1)), array(), true);
return $count;
}
the first way is the classic way of incrementing. Get counter, increment, update with set.
The second way is calling update with an extra hidden field.
So the function definition for update is
public function update($collection = '', $data = array(), $options = array(), $literal = false) What we want to do in JSON is db.counter.update({“name”:”about”}, {$inc : {“count” : 1}})
So lets break it down
where(array('name'=>'about')) = {“name”:”about”}
array('$inc' => array('count'=>1)) = {$inc : {“count” : 1}}
Then i pass in a blank array(), because i don’t have any options
Then i pass in a true so $literal = true.
THIS is what allows us to use $inc, otherwise it defaults to $set.
So we have a model, but it’s not getting anything from the database. Let’s get that counter from our mongoDB. Again here is our controller code
Class Controller_About extends Controller
{
public function action_index()
{
$data = array();
$data['name'] = "CopyPasteCoder";
$data['age'] = 27;
//echo "This is my first attempts at MVC architecture.";
return View::forge('about', $data);
}
public function action_water()
{
$result = Model\About::get_count();
echo "i'm thirsty $result";
}
public function action_supMe($name = '')
{
$data = array();
$data['name'] = $name;
$data['age'] = 27;
return View::forge('about', $data);
}
}
And our model looks like this
namespace Model;
class About extends \Model
{
public static function get_count()
{
$count = 56; //Will get this from the DB SOON.
return $count;
}
}
Now let’s modify our model to get data from the DB with this code.
namespace Model;
class About extends \Model
{
public static function get_count()
{
$mongodb = \Mongo_Db::instance('default');
$result = $mongodb->where(array("name"=>'about'))->get('counter');
$count = $result[0]['count'];
return $count;
}
}
The first line instantiates our mongoDB object that we will use to run queries. Note the capitalization on Mongo_Db and the namespace ‘'. We get the default instance, as defined in our config/db.php file.
The next line we store our queried data into an array called $result.
When calling the mongodb object, you can specify a where clause before the get. Then because MongoDB uses JSON to query data, we need to send in arrays.
this is pretty much doing db.counter.find({“name”:”main”})
The result set is an array, and i only expect 1 row. So i just pull the count column from the first row.
then i return this data. That’s it, it should be pulling back 1!
So at this point, you already have a view, and a controller, but aren’t using any models yet. Let’s make a simple model for our controller to call. Again here is our controller code
Class Controller_About extends Controller
{
public function action_index()
{
$data = array();
$data['name'] = "CopyPasteCoder";
$data['age'] = 27;
//echo "This is my first attempts at MVC architecture.";
return View::forge('about', $data);
}
public function action_water()
{
echo "i'm thirsty";
}
public function action_supMe($name = '')
{
$data = array();
$data['name'] = $name;
$data['age'] = 27;
return View::forge('about', $data);
}
}
Now go to /fuelphp/fuel/apps/classes/model/ and create about.php then drop this code in there
namespace Model;
class About extends \Model
{
public static function get_count()
{
$count = 56; //Will get this from the DB SOON.
return $count;
}
}
At the time of me writing this, i don’t know why we have to use the namespace Model and extend using \Model. I understand that if we are in namespace Model we do the \Model to retrieve the Model class that is outside of our namespace. But why do I have to use namespace Model in the first place. Why can’t i just extend Model, the way controller does? I tried changing the namespaces, but it never seems to work.Update below! But to get data back from the model, let’s just edit our water function above, and get some data back.
public function action_water()
{
$result = Model\About::get_count();
echo "i'm thirsty $result";
}
Just like that we got data back from our Model, pretty easy. One thing, we could do is at the top of our controller file is add use \Model\About;. This lets our controller know about the About model, and let’s us reference it like About::get_count();
So we made a call to our model to get back some data;
“big deal, I could have just done it in the Controller” You could have, but with separate functions and a separate class, it will make things a little cleaner. It separates duties. So the model can get the data, clean it up. The Controller can take the data, analyze it, then decide what to display. Then the controller can send it to a view for consumption. It is much cleaner than all of this happening in one function.
Again can somebody can explain the reason I can’t use other namespaces for Model (if i figure it out i’ll make a post and let erryone know)?
**UPDATE: I went over to the Forums at FuelPHP, and a bright guy by the name of WanWizard explained it all.
Basically its all about filename resolution using the class/namespaces.
so I could use
class Model_About extends Model {...}
and call it using \Model_About::get() from the controller
or
use namespace Model;
class About extends \Model{...}
and call it using Model\About::get() from the controller
and in both cases it would call /fuel/app/classes/models/about.php
And in my above example if i created a directory /fuel/app/classes/models
i could put my about.php in there. And use namespace Models if i wanted.
We want to create a database now to insert data into. Most people go with the blog concept, and I will do the same for now. But this is only going to be a very basic setup. We are just gonna create the DB and a collection for us to use.
Pop into your terminal/shell program or RockMongo. However you can manually insert some data our MongoDB server. I’m gonna assume you are using the shell here and use shell commands.
>sudo mongo
>use blog
>db.counter.insert({"name":"about", "count": 1})
>db.counter.find()
What we just did was go into out mongo terminal, changed our working db to blog, and inserted a collection called counter, with a record Name=Main, and Count=1.
Note the JSON syntax. (if you don’t know JSON, you gotta learn that to use mongoDB.)
until we insert something into our database, it isn’t saved. Thats why we manually did it.
Note the 1 after “count”: has no quotes. **Important. This way its saved as an int, instead of string
If you are looking for an administrative web GUI for mongoDB, i recommend using RockMongo. It has a familiar layout to phpMyAdmin, and is also very clean. It looks like you can administer multiple servers as well. It’s painless to install, just drop it in and modify the config.php. There is another option, phpmoadmin, but the UI could use some work. It’s nice because its fast and its 1 file to just drop in, but again the UI isn’t as obvious, or powerful ( from first looks)
You may have asked the question, How do I move my fuelphp public directory?
It’s easy, it’s all in the /fuelphp/fuel/public/index.php First decide where you want to put it, i chose up one level, so then I wouldn’t have to type public anymore. So now i could access my site at http://192.168.2.7/fuel/ instead of http://192.168.2.7/fuel/public So move /fuelphp/fuel/public/index.php up one level, and then open it to edit it. and look around line 13 for stuff that looks like this
...
/**
* Path to the application directory.
*/
define('APPPATH', realpath(__DIR__.'/fuel/app/').DIRECTORY_SEPARATOR);
/**
* Path to the default packages directory.
*/
define('PKGPATH', realpath(__DIR__.'/fuel/packages/').DIRECTORY_SEPARATOR);
/**
* The path to the framework core.
*/
define('COREPATH', realpath(__DIR__.'/fuel/core/').DIRECTORY_SEPARATOR);
...
Notice how I have modified the PATH definitions to remove the relative path going up one level.
If you aren’t sure what relative paths are, i’ll just give a quick example of what happened.
__DIR__ prints out the current directory /fuelphp/fuel/public/ using ../ takes us up one level to /fuelphp/fuel/ then the /fuel/core/ takes us to /fuelphp/fuel/fuel/app/
If you still don’t understand, read up on relative paths. Or shoot me a message.
This resulted in this error
Fuel\Core\Mongo_DbException [ Error ]: Unable to connect to MongoDB: Operation now in progress
That was the address of my local dev server, where the mongoDB was hosted, as well as my LAMP server.
turns out what I needed to do was changed it to use localhost
it worked easily after that. But if you want to have it set to an ip, let’s say your DB server is another server. In that case you gotta edit this file in your mongodb configuration.
sudo nano /etc/mongodb.conf
look for the bind_ip = 127.0.0.1 This means it will only allow connections from localhost. So in my case I changed it tobind_ip =192.168.2.7.
But this only allows connections on that one IP. So if i comment out that line #bind_ip =127.0.0.1 then it will allow connections from anywhere.
Commenting it out is equivalent to setting it bind_ip = 0.0.0.0 If you don’t have a database setup yet, we can set that up later, but you can put that there for now.
Before I jump into models, lets have a little fun with routing. Right now if you been following along, we got a view
<h1>About</h1>
<hr>
<p><blockquote>Created using FuelPHP. An experiment into the MVC architecture</blockquote></p>
<br><?php echo $name ?>
<br><?php echo $age ?>
and controller
Class Controller_About extends Controller
{
public function action_index()
{
$data = array();
$data['name'] = "CopyPasteCoder";
$data['age'] = 27;
//echo "This is my first attempts at MVC architecture.";
return View::forge('about', $data);
}
public function action_water()
{
echo "i'm thirsty";
}
public function action_supMe($name = '')
{
$data = array();
$data['name'] = $name;
$data['age'] = 27;
return View::forge('about', $data);
}
}
Lets take a look at routing. Pop open /fuelphp/fuel/app/config/routes.php
it should look something like this
Notice the 2 lines with funky in them. I have added a route, so anytime you want to access
...index.php/about you can type in
...index.php/funky and it will take you to the same place.
Note I had to add 2 routes. The main one ‘funky => ‘about’ routes the main action_index()
The next one ‘funky/(:any)’ => ‘about/$1’ routes any other functions, like action_water, and action_supMe($name)…
Call supme with a name, see it work!
So we re-routed the user to the same pages using a router
“mehh, I could have just called it funky if I wanted” I haven’t thought of any good reason for routing yet, but i’m thinking if you have to move directories of files, or maybe if you had some kind of clustered setup. I’m not sure yet, but it’s some powerful stuff. Does anybody have a good example of routing they can tell me?