Giter Site home page Giter Site logo

denchikby / phalcon-mongodb-odm Goto Github PK

View Code? Open in Web Editor NEW
45.0 6.0 9.0 41 KB

MongoDB ODM for Phalcon framework for new mongodb php extension with query builder and rich functionality

License: MIT License

PHP 100.00%
phalcon mongodb-odm query-builder phalcon-framework php mongodb odm mongodb-orm builder mongodb-driver

phalcon-mongodb-odm's Introduction

Phalcon MongoDB ODM

Latest Stable Version Total Downloads Latest Unstable Version License

Tiny, simple and functional MongoDB ODM library for Phalcon framework for new mongodb php extension

Installation

Make sure you have the MongoDB PHP driver installed. You can find installation instructions at http://php.net/manual/en/mongodb.installation.php

Install the latest stable version using composer:

composer require denchikby/phalcon-mongodb-odm

or

{
    "require": {
        "denchikby/phalcon-mongodb-odm": "dev-master"
    }
}

Configuration

Add settings and service to DI:

$di->set('config', function () {
    return new \Phalcon\Config([
        'mongodb' => [
            'host'     => 'localhost',
            'port'     => 27017,
            'database' => 'auto'
        ]
    ]);
}, true);

$di->set('mongo', function () use ($di) {
    $config  = $di->get('config')->mongodb;
    $manager = new \MongoDB\Driver\Manager('mongodb://' . $config->host . ':' . $config->port);
    return $manager;
}, true);

Creating Model

use DenchikBY\MongoDB\Model;

class User extends Model {}

in this case will be used 'user' collection with same name as model.

To specify another collection name use getSource method:

use DenchikBY\MongoDB\Model;

class User extends Model
{
    public static function getSource()
    {
        return 'users';
    }
}

Initialize Model

To initialize a new model instead of

$user = new User;

use

$user = User::init();

Initialize filled model:

$user = User::init(['name' => 'DenchikBY']);

or

$user = User::init()->fill(['name' => 'DenchikBY']);

or init and save in db

$user = User::create(['name' => 'DenchikBY']);

Methods

To array:

$ad = Ads::init()->first();
var_dump($ad->toArray()); // array of all fields
var_dump($ad->toArray(['include' => ['id', 'name']])); // array of specified fields
var_dump($ad->toArray(['exclude' => ['user_id']])); // array of all fields except specified

Unset field:

$ad = Ads::init()->first();
$ad->unsetField('counters.views');

Attributes Casting

It allow you to modify attribute type on setting/filling:

It help to save fields to db with need type, what is very important, cause mongo don't casting types in queries.

Supported types: integer, float, boolean, string, array, object, id

class User extends Model
{
    protected static $casts = [
        'age' => 'integer'
    ];
}

$user->age = '20';

var_dump($user->age); => int(20)

Casts also work in where methods of builder:

User::where('age', '20')->get();

age will converted to integer and query will load normally.

Relations

There are two types of relations: one and many;

Definition:

field => [related model, type, local field, foreign field]

public static $relations = [
    'user'     => [Users::class, 'one', 'user_id', '_id'],
    'comments' => [Comments::class, 'many', '_id', 'ad_id']
];

Relations can be loaded by two ways:

By one query:

Ads::where('views', '>', 1000)->join('user')->join('comments')->get()

it will use $lookup operator of aggregation framework.

By several queries, just call attribute with name of key in relations array:

$user = User::where('name', 'DenchikBY')->first();
var_dump($user->comments);

Scopes

Scopes help to put common queries to methods:

/**
 * @method $this active()
 */
class BaseModel extends Model
{
    public scopeActive($builder)
    {
        return $builder->where('active', 1);
    }
}

$users = User::active()->get();
$ads   = Ads::active()->get();

Global scopes

This scope will binded to any query of model:

class Ads extends Model
{
    public static $globalScopes = ['notDeleted'];
    
    public function notDeleted($builder)
    {
        return $builder->where('deleted', 0);
    }
}

Mutators (getters/setters)

Mutators allow modify attributes when you getting, setting or filling it.

For example, when you creating user and set the password, hashing may be defined in model:

$user = User::create([
    'name'     => 'DenchikBY',
    'password' => '1234'
]);
class User extends Model
{
    public function getName($value)
    {
        return ucfirst($value);
    }

    public function setPassword($value)
    {
        return Di::getDefault()->get('security')->hash($value);
    }
}

Events

Existed events before/after for actions save, create, update, delete.

class User extends Model
{
    public function afterCreate()
    {
        Email::send($this->email, 'emails.succeddfull_registration', ['user' => $this]);
    }
}

Query Builder

Query builder could be called clearly or implicitly.

$users = User::query()->get();

similar

$users = User::get();

Usage

$builder = User::query();

//allowed operators in where =, !=, >, <, >=, <=

$builder->where('name', '=', 'DenchikBY');
//similar
$builder->where('name', 'DenchikBY');

$builder->orWhere('name', 'Denis');

$builder->betweenWhere('age', 20, 30);

$builder->notBetweenWhere('age', 20, 30);

$builder->inWhere('name', ['DenchikBY', 'Denis']);

$builder->notInWhere('name', ['DenchikBY', 'Denis']);

$builder->orderBy('created_at', 'desc');

$builder->limit(2, 1);

//Closing methods:

$users = $builder->get(); // return collection of models

$user = $builder->first(); // return first model without collection

$count = $builder->count(); // run count command, which return int of counted documents

$count = $builder->increment('coins', 10); // increase field in founded documents, return count of them

$count = $builder->decrement('coins', 10);

$count = $builder->update(['banned' => 1]); // update founded documents with specified fields, return count of them

$count = $builder->delete(); // delete founded documents, return count of them

$age = $builder->max('age');

$age = $builder->min('age');

$age = $builder->avg('age');

$total = $builder->sum('age');

$builder->unsetField('counters.views');

Advanced Wheres

For grouping where conditions:

$query = Ads::query()->where('auto_id', '567153ea43946846683e77ff')->where(function (Builder $query) {
    $query->where('body', 1)->orWhere('capacity', 2);
});

Query Result Collection

Every select query will return iterated collection class of models.

$collection = Comments::where('ad_id', new \MongoDB\BSON\ObjectId($id))->get();

It could be iterated with foreach, or used as array $collection[0]->name;

Methods

Size of collection:

$collection->count();

Will return array of assocs of each model:

$collection->toArray();

Return json of array, created by toArray method:

$collection->toJson();

Eager loading, similar as join:

Will load all for all comments by single query and put necessary into every document.

$collection->eager(Users::class, 'user', 'user_id', '_id');

Grouping documents to arrays by specific field:

$collection->groupBy('user');

Keys the collection by the given key (unlike groupBy that value will single model, in groupBy array of models):

$collection->keyBy('user');

Return array of values specific field of collection:

$collection->pluck('user_id');

Return associated array of specified key => value fields:

$collection->combine('_id', 'text');

Return array of chunked collection by specified size:

$collection->chunk(10);

phalcon-mongodb-odm's People

Contributors

denchikby avatar uzrnem avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

phalcon-mongodb-odm's Issues

Init and static construction

Using the init method fails:

class Users extends DenchikBY\MongoDB\Model
{
}

$users = Users::init()->first();    	
var_dump($users);exit;

Fatal error: Uncaught Error: Call to undefined method Users::first() in /vagrant/html/app/controllers/AuthController.php:9 Stack trace: #0 [internal function]: AuthController->indexAction() #1 [internal function]: Phalcon\Dispatcher->callActionMethod(Object(AuthController), 'indexAction', Array) #2 [internal function]: Phalcon\Dispatcher->_dispatch() #3 [internal function]: Phalcon\Dispatcher->dispatch() #4 /vagrant/html/public/mvc.php(44): Phalcon\Mvc\Application->handle() #5 {main} thrown in /vagrant/html/app/controllers/AuthController.php on line 9

I cannot understand the Model init method, it calls a 'new static' construction, but there's no constructor method defined in Model, and the parent (Collection) constructor doesn't expects the parameters given by the init method. I'm using php 7.1 but it fails also in php 5.6

Doing update instead of insert when Model::create()

When I used Model::create(), specifying _id uses the updateOne function in MongoDB driver, which leads to silent failure if the _id does not exist in the collection.

To me, this is kind of counter-intuitive. The method is named 'create'. It should mean inserting a new record. What if I want to use a specific type, e.g. incremental integer, as primary key? Seems there's no choice for users.

I'd suggest separating create and update action.

Using $this when not in object context

Hi, just run into your library, and seeing that Phalcon still doesn't support the new MongoDB driver, I gave it a try. Sadly, I've run into the following exception, I'm not doing anything other than follow the example to set it up (e.g., the model class, and the setting of the $db in the index.php, e.g.:

$di->set('config', function () {
    return new \Phalcon\Config([
        'mongodb' => [
            'host' => 'localhost',
            'port' => 27017,
            'database' => 'pithea'
        ]
    ]);
}, true);

$di->set('mongo', function () {
    $config = $this->get('config')->mongodb;
    $manager = new \MongoDB\Driver\Manager('mongodb://' . $config->host . ':' . $config->port);
    return $manager;
}, true);

And my model (app/models/Prices.php);

require __DIR__ . '/vendor/autoload.php';

use DenchikBY\MongoDB\Model;

class Prices extends Model
{
    public $symbol;
    public $data;

    public function initialize()
    {
        $this->setSource("prices");
    }
}

And the view controller:

    public function pricesAction()
    {
        $this->view->pick('ajax/prices');
        $items = Prices::find();
        echo "There are ", count($items), "\n";
    }

Regardless of what I try, I keep getting:

PHP Fatal error:  Uncaught Error: Using $this when not in object context in app/models/vendor/denchikby/phalcon-mongodb-odm/src/MongoDB/Model.php:74

Any chance this can be fixed?

_id is not always ObjectId()

In some cases _id can be just a string. But in methods findById and others hardcoded ObjectId($id) is used. So when I try to find by String, the response is empty.

limit()method is not work.

hello, when i use limit() method query ,i get return results is not what i want .
public static function queryNearby($longitude, $latitude, $maxdistance, $pageSize = 10, $pageNumber = 0)
{
$param = array(
'loc' => array(
'$nearSphere' => array(
'$geometry' => array(
'type' => 'Point',
'coordinates' => array(doubleval($longitude), doubleval($latitude)),
),
'$maxDistance' => $maxdistance //$maxdistance*1000
)
)
);
$columns = ['id','store_name','address','commercial_activities','status','loc','district_no'];
$cursor = Stores::query()->where($param)->columns($columns)->limit((int)$pageSize,(int)($pageNumber))->get();
$result = $cursor->toArray();
return $result;
}

Database Error handling

What I see is, If something's gone wrong during queries/inserts/updates when database error occurs (e.g. trying to update non-existent row), things just fails silently.

Printing some sort of stacktrace or even error code would be nice! It's quite hard to debug when something unexpected occurs.

Please point out if I'm wrong. Thanks.

on create method afterCreate and afterSave events are duplicated

On create method "afterCreate" and "afterSave" events are duplicated.

public static function create(array $attributes)
    {
        $model = static::init($attributes)->save();
        $model->event('afterCreate');
        $model->event('afterSave');
        return $model;
    }

same events are called inside the save() function;

public function save(array $attributes = null)
    {
        if ($attributes != null) {
            $this->fill($attributes);
        }
        $this->event('beforeSave');
        if (isset($this->_id)) {
            $this->event('beforeUpdate');
            $this->updateOne(['_id' => $this->_id], ['$set' => $this->_attributes]);
            $this->event('afterUpdate');
        } else {
            $this->event('beforeCreate');
            $this->fill($this->insertOne($this->_attributes));
            $this->event('afterCreate');
        }
        $this->event('afterSave');
        return $this;
    }

Error

MongoDB\Model.php line 26

$model = (new static(Di::getDefault()->get('mongo'), static::getDbName() . '.' . static::getSource()));

should be

$model = (new static(Di::getDefault()->get('mongo'), static::getDbName(), static::getSource()));

getId() returns empty string after save()

Hi, thanx for your project, here the source:

        $salon = Salon::init();

        $salon->title = $this->request->get('title');
        $salon->tel = $this->request->get('tel');
        $salon->latitude = $this->request->get('latitude');
        $salon->longitude = $this->request->get('longitude');
        $salon->address = $this->request->get('address');
        $salon->action = $this->request->get('action');
        $salon->save();   

       // returns empty string
       $salon->getId();

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.