Name

QBit::Application::Model::DBManager - Class for smart working with DB.

GitHub

https://github.com/QBitFramework/QBit-Application-Model-DBManager

Install

  • cpanm QBit::Application::Model::DBManager

  • apt-get install libqbit-application-model-dbmanager-perl (http://perlhub.ru/)

For more information. please, see code.

Package methods

init

Initialization model.

It is done:

  • Init fields

No arguments.

Example:

my $model = Application::Model->new(); # Application::Model based on QBit::Application::Model

# but usually this is used so

package Application;

use base qw(QBit::Application);

__Package__->set_accessors(model => {package => 'Application::Model'});

...

# after

my $model = $app->model;

model_fields

Set model fields. Save into package stash with key __MODEL_FIELDS__

Arguments:

  • %fields - Fields (type: hash)

Example:

package Sellers;

use base qw(QBit::Application::Model::DBManager);

__PACKAGE__->model_accessors(
    db    => 'Application::Model::DB',    # your DB model, see QBit::Application::Model::DB
    items => 'Application::Model::Items', # your model (base from QBit::Application::Model::DBManager)
);

__PACKAGE__->model_fields(
    id => {
        pk      => TRUE,      # primary key for this model
        db      => 'sellers', # this field is from the table
        default => TRUE,      # this field returns if fields were not requested
    },
    caption => {
        db           => 'sellers', # this field is from the table
        default      => TRUE,      # this field returns if fields were not requested
        i18n         => TRUE,      # this field depends on current locale, (in DB this field i18n too)
        check_rights => 'sellers_view_field__caption',
        # your right for "caption", see check_rights from QBit::Application
        # Try not to use this key.
    },
    id_with_caption => {
        depends_on => [qw(id caption)], # this field depends on "id" and "caption"
        get => sub {
            my $fields = shift; # object QBit::Application::Model::DBManager::_Utils::Fields
            # access to model: $fields->model

            my $row = shift; # hash from db: {id => 1, caption => 'Happy Milkman'}

            return $row->{'id'} . ': ' . $row->{'caption'};
        }
    },
    id_with_caption_db => {
        db => 'sellers',
        db_expr => {CONCAT => ['id', \': ', 'caption']}, # see QBit::Application::Model::DB::Query
    },
    name => {
        # relation "one to one". Use it if you want use join
        db => 'users', # this field is from the table, but the tables are different
    },
    items => {
        # relation "one to one", "one to many" or "many to many"
        depends_on => [qw(id)],
        get => sub {
            my $fields = shift; # object QBit::Application::Model::DBManager::_Utils::Fields
            my $row = shift; # hash from db: {id => 1}

            # $fields->{'__ITEMS__'} created in pre_process_fields
            return $fields->{'__ITEMS__'}{$row->{'id'}} // [];
        }
    }
);

# returns query (class: QBit::Application::Model::DB::Query)
sub query {
    my ($self, %opts) = @_;

    my $filter = $self->db->filter($opts{'filter'});

    unless ($self->check_rights('sellers_view_all')) {
        my $cur_user = $self->cur_user();

        $filter->and({user_id => $cur_user->{'id'}};
    }

    my $query = $self->db->query->select(
        table  => $self->db->sellers,
        fields => $opts{'fields'}->get_db_fields('sellers'), # returns db expression for fields with "db" = 'sellers'
        filter => $filter
    );

    my $users_fields = $opts{'fields'}->get_db_fields('users');

    # join users only if needed (field "name" was requested)
    $query->join(
        table  => $self->db->users,
        fields => $users_fields,
    ) if %$users_fields;

    return $query;
}

# used for dictionaries
sub pre_process_fields {
    my $self   = shift; # model
    my $fields = shift; # object QBit::Application::Model::DBManager::_Utils::Fields
    my $result = shift; # data from db

    if ($fields->need('items')) {
        # gets items only if needed (field "items" was requested)

        my $items = $self->items->get_all(
            fields => [qw(id seller_id caption)],
            filter => {seller_id => [map {$_->{'id'}} @$result]}, # key "id" exists because fields "items" depends on "id"
        );

        # create dictionaries {<SELLER_ID> => <ITEM>}
        $fields->{'__ITEMS__'} = {map {$_->{'seller_id'} => $_} @$items};
    }
}

TRUE;

# in your code

my $sellers = $app->sellers->get_all(fields => [qw(id id_with_caption_db name items)]);

#$sellers = [
#    {
#        id => 1,
#        id_with_caption_db => '1: Happy Milkman',
#        name  => 'Petr Ivanovich',
#        items => [
#            {
#                id        => 1,
#                seller_id => 1,
#                caption   => 'milk'
#            },
#            {
#                id        => 2,
#                seller_id => 1,
#                caption   => 'cheese'
#            },
#        ],
#    }
#]

init_fields

Initialization fields. Used after calling model_fields in run time a code

No arguments.

Example:

$model->model_fields(...);
$model->init_fields();

model_filter

Set model filters. Save into package stash with key __DB_FILTER__

Types: namespace (QBit::Application::Model::DBManager::Filter)

  • boolean

  • dictionary

  • multistate

  • number

  • subfilter

  • text

Arguments:

  • %opts - Options (type: hash)

    • db_accessor - name db accessor

    • fields - filter fields

Example:

__PACKAGE__->model_filter(
    db_accessor => 'db', # your db accessor
    fields      => {
        id      => {type => 'number'},
        caption => {type => 'text'},
        active  => {type => 'boolean'},
        product => {
            type   => 'dictionary',
            values => sub {
                [
                    {id => 1, label => gettext('Milk')},
                    {id => 2, label => gettext('Cheese')},
                ];
            },
        }
        multistate => {type => 'multistate'},
        # you can filtered by field from other model
        user       => {
            type           => 'subfilter',
            model_accessor => 'users',   # accessor related model
            field          => 'user_id', # field from this model
            fk_field       => 'id',      # field from model "users"
        },
    },
);

# in your code

my $items = $app->model->get_all(
    filter => [
        'OR',
        [
            ['id',      '=',    1],
            ['caption', 'LIKE', 'Nike'],
            ['active',  '=',    1],
            ['product', '=', [1, 2]],
            ['multistate', '=', 'approved and working'],
            ['user', 'MATCH', ['login', '=', 'ChuckNorris']] # login is a filter in model "users"
        ]
    ]
);

get_model_fields

Returns a model fields.

No arguments.

Return value: $model_fields (type: ref of a hash)

Example:

my $model_fields = $app->model->get_model_fields(); # getter for method "model_fields"

get_all

Returns model items.

Arguments:

  • %opts - Options (type: hash)

    • fields

      returns "id" and "caption"
      my $data = $app->model->get_all(fields => [qw(id caption)]);
      
      # returns fields with key "default"
      my $data = $app->model->get_all();
      
      # return all fields
      my $data = $app->model->get_all(fields => ['*']);
    • filter - see QBit::Application::Model::DB::Query. Unlike filters from the database, model filters can not use field names and scalars are used without reference.

      # mysql: name = caption
      # db:    ['name', '=', 'caption']
      # model: no way
      
      # mysql: id = 12
      # db:    ['id', '=', \12]
      # model: ['id', '=', 12]
      
      my $data = $app->model->get_all(filter => {id => 1});
    • distinct - unique rows from table

      my $data = $app->model->get_all(fields => [qw(caption)], distinct => TRUE);
    • for_update - get lock

      # get
      my $data = $app->model->get_all(fields => [qw(id)], filter => ["caption", "LIKE", "milk"]}, for_update => TRUE);
      
      # update
      $app->db->table->edit($app->db->filter({id => [map {$_->{'id'}} @$data]}), {caption => 'Milk'});
    • order_by - set order

      my $data = $app->model->get_all(
          fields => [qw(caption)],
          order_by => [
              'caption', # asc
              [
                'price', # field
                1        # order: 0 - asc, 1 - desc
              ]
          ]
      );
    • limit

      my $data = $app->model->get_all(limit => 100);
    • offset

      my $data = $app->model->get_all(limit => 100, offset => 1000);
    • calc_rows

      my $data = $app->model->get_all(limit => 100, calc_rows => TRUE);
      
      my $all_data = $app->model->found_rows(); # 1_000_000
    • all_locales

      my $data = $app->model->get_all(fields => [qw(id caption)], all_locales => TRUE);
      
      #$data = [
      #    {
      #        id      => 1,
      #        caption => {
      #            ru => 'Веселый молочник',
      #            en => 'Happy Milkman',
      #        },
      #    },
      #    ...
      #]

Return value: Data (type: ref of a array)

Example:

my $data = $app->model->get_all(
    fields => [qw(id caption)],
    filter => ['OR', [
      ['id', '=', 10],
      ['caption', '=', 'milk']
    ]],
    limit    => 100,
    offset   => 10_000,
    order_by => ['caption']
);

found_rows

Returns count of a rows.

No arguments.

Return value: $found_rows (type: scalar or undef)

Example:

my $data = $app->model->get_all(limit => 3, calc_rows => TRUE);

my $found_rows = $app->model->found_rows();

last_fields

Returns a last fields was requested.

No arguments.

Return value: $last_fields (type: scalar or undef)

Example:

my $data = $app->model->get_all(fields => [qw(id caption)]);

my $last_fields = $app->model->last_fields();

# $last_fields = {
#     id      => '',
#     caption => '',
# };

get

Returns row by primary key.

Arguments:

  • $pk - primary key (type: scalar or hash)

  • %opts - options (type: hash; see get_all)

Return value: Row (type: ref of a hash or undef)

Example:

my $item = $app->model->get(1, fields => [qw(id caption)]);

# or
my $item = $app->model->get({id => 1}, fields => [qw(id caption)]);

get_pk_fields

Returns primary keys.

No arguments.

Return value: fields (type: ref of a array)

Example:

my $pk = $app->model->get_pk_fields(); # ['id']

pre_process_fields

used for dictionaries.

No arguments.

Return value: undef

Example:

# see method: model_fields
$app->model->pre_process_fields();