I'm trying to add pagination to my code igniter project. I am using Doctrine for my models and I can't seem to use $this->load->model('gif') to access the methods in my controller. I guess a Doctrine model acts differently, but surely there is a way to call the public methods?
Here is my controller:
<?php
class View extends Controller
{
function index()
{
// load pagination class
$gifs = Doctrine::getTable('Gif')->findAll();
$this->load->library('pagination');
$config['base_url'] = base_url().'view/';
$config['total_rows'] = count($gifs);
$config['per_page'] = '5';
$config['full_tag_open'] = '<p>';
$config['full_tag_close'] = '</p>';
$this->pagination->initialize($config);
//load the model and get results
//$this->load->model('gif');
$data['results'] = $gifs->getGifs($config['per_page'],$this->uri->segment(2));
// load the view
$this->load->view('front_images', $data);
}
}
Here is my model
<?php
class Gif extends Doctrine_Record {
public function setTableDefinition()
{
$this->hasColumn('photo_path', 'string', 255, array('unique' => true, 'notnull' => true));
$this->hasColumn('title', 'string', 255, array('notnull' => true));
$this->hasColumn('user_id', 'integer', 4);
$this->hasColumn('token', 'string', 255);
}
public function setUp()
{
$this->actAs('Timestampable');
$this->hasOne('User', array(
'local' => 'user_id',
'foreign' => 'id'
));
}
public function preInsert($event)
{
$this->token = (sha1(rand(11111, 99999)));
}
public function numGifs() {
$result = Doctrine_Query::create()
->select('COUNT(*) as num_gifs')
->from('Gif')
->fetchOne();
return $result['num_gifs'];
}
public function getGifs($offset, $limit)
{
$gifs = Doctrine_Query::create()
->from('Gif g')
->orderBy('g.created_at DESC')
->limit($limit)
->offset($offset)
->execute();
return $gifs;
}
}
How can I call the numGifs and getGifs methods from that controller? Thanks in advance!
I am also using CI in conjunction with doctrine. for reference i am following the tuto located at http://www.phpandstuff.com/articles/codeigniter-doctrine-from-scratch-day-1-install-and-setup .
I don't know if you followed similar steps but using this approach models do not need to be loaded but rather instantiated.
for eg.
$g = new Gif();
$g = $g->getGifs();
(although in this particular case
- $g expects only one row
- Am not sure if we can define getter functions inside the model representing the table itself. in the tuto am following the model contains only the db table definition as well as any relationships)
hope this helps.
Related
I have a model that has a one to many relationship to the versions of the description.
In my Controller
$tag = Tags::create([
'name' => $request->get('name'),
'user_id' => \Auth::id(),
]);
$tag->update([
'content' => $request->get('description')
]);
In my Model:
public function setContentAttribute(string $value)
{
$this->versions()->create([
'user_id' => \Auth::id(),
'value' => $value
]);
}
So I can't put content directly as an attribute in the create method because there is no Model right now.
But is it possible to overwrite the create Method?
When I try to overwrite something like this in my Model it will do an infinity loop
public static function create($attr) {
return parent::create($attr);
}
So my question is if it is possible to have something like this:
$tag = Tags::create([
'name' => $request->get('name'),
'user_id' => \Auth::id(),
'content' => $request->get('content')
]);
and in the Model:
public static function create($attr) {
$value = $attr['content'];
$attr['content'] = null;
$object = parent::create($attr);
$object->content = $value;
$object->save();
return $object;
}
Update
I didn't overwrite the create method but called it customCreate. So there is no infinity loop anymore and I can pass all variables to the customCreate function that handles the relationships for me.
Solution
After reading the changes from 5.3 to 5.4 it turns out that the create method was moved so you don't have to call parent::create() anymore.
The final solution is:
public static function create($attr) {
$content = $attr['content'];
unset($attr['content']);
$element = static::query()->create($attr);
$element->content = $content;
$element->save();
return $element;
}
I don't see why not and you could probably implement a more general approach? Eg. checking if set{property}Attribute() method exists, if it does - use it to assign a value, if it doesn't - use mass assigning.
Something like:
public static function create($attr) {
$indirect = collect($attr)->filter(function($value, $property) {
return method_exists(self::class, 'set' . camel_case($property) . 'Attribute');
});
$entity = parent::create(array_diff_key($attr, $indirect->toArray()));
$indirect->each(function($value, $property) use ($entity) {
$entity->{$property} = $value;
});
$entity->save();
return $entity;
}
I haven't really tested it but it should work. I use something like this in one of my Symfony apps.
Im trying to pass in a table name to my model, as the model operates on two tables, but has the same methods.
I do it like so:
$this->model = new Emotions(array('section' => 'red'));
And in the model I set the table like:
public function __construct($attributes = array(), $exists = false){
parent::__construct($attributes, $exists);
$this->table = $attributes['section'];
}
But I get the error:
Undefined index: section
Any ideas where I'm going wrong?
Yes i get it, This class maybe running twice.
Please try this.
public function __construct($attributes = array(), $exists = false){
parent::__construct($attributes, $exists);
if(isset($attributes['section'])) {
$this->table = $attributes['section'];
}
}
My personal suggestion
<?php
class Emotions extends Eloquent
{
public function setTableName($name)
{
$this->table = $name;
return $this;
}
}
And you can use like this
$emotion = new Emotions(array('foo' => 'bar'))
->setTableName('blabla')
->save();
add below line to your class.
protected $fillable = array('section');
http://laravel.com/docs/eloquent#mass-assignment
I used to use Yii framework. I would like to make project using Phalcon. I could not find validation scenario on Phalcon. What is the best way to correctly implement it on Phalcon?
Thanks in advance.
Any data validation:
<?php
use Phalcon\Validation\Validator\PresenceOf,
Phalcon\Validation\Validator\Email;
$validation = new Phalcon\Validation();
$validation->add('name', new PresenceOf(array(
'message' => 'The name is required'
)));
$validation->add('email', new PresenceOf(array(
'message' => 'The e-mail is required'
)));
$validation->add('email', new Email(array(
'message' => 'The e-mail is not valid'
)));
$messages = $validation->validate($_POST);
if (count($messages)) {
foreach ($messages as $message) {
echo $message, '<br>';
}
}
http://docs.phalconphp.com/en/1.2.6/reference/validation.html
If you are working with models:
<?php
use Phalcon\Mvc\Model\Validator\InclusionIn,
Phalcon\Mvc\Model\Validator\Uniqueness;
class Robots extends \Phalcon\Mvc\Model
{
public function validation()
{
$this->validate(new InclusionIn(
array(
"field" => "type",
"domain" => array("Mechanical", "Virtual")
)
));
$this->validate(new Uniqueness(
array(
"field" => "name",
"message" => "The robot name must be unique"
)
));
return $this->validationHasFailed() != true;
}
}
http://docs.phalconphp.com/en/1.2.6/reference/models.html#validating-data-integrity
models also have events, so you can add any logic you need in these functions:
http://docs.phalconphp.com/en/1.2.6/reference/models.html#events-and-events-manager
I would like to use forms for CRUD as they are very dynamic and reusable.
You can achieve that in forms using options.
You can pass additional options to form and act like a scenario.
You can check Form constructor here
https://docs.phalconphp.com/en/latest/api/Phalcon_Forms_Form.html
In your controller you can pass $options
<?php
use Phalcon\Mvc\Controller;
class PostsController extends Controller
{
public function insertAction()
{
$options = array();
$options['scenario'] = 'insert';
$myForm = new MyForm(null, $options);
if($this->request->hasPost('insert')) {
// this will be our model
$profile = new Profile();
// we will bind model to form to copy all valid data and check validations of forms
if($myForm->isValid($_POST, $profile)) {
$profile->save();
}
else {
echo "<pre/>";print_r($myForm->getMessages());exit();
}
}
}
public function updateAction()
{
$options = array();
$options['scenario'] = 'update';
$myForm = new MyForm(null, $options);
}
}
And your form should look like something this
<?php
// elements
use Phalcon\Forms\Form;
use Phalcon\Forms\Element\Text;
// validators
use Phalcon\Validation\Validator\PresenceOf;
class MyForm extends Form {
public function initialize($entity = null, $options = null) {
$name = new Text('first_name');
$this->add($name);
if($options['scenario'] == 'insert') {
// at the insertion time name is required
$name->addValidator(new PresenceOf(array('message' => 'Name is required.')));
}
else {
// at the update time name is not required
// as well you can add more additional validations
}
}
}
now you can add multiple scenarios and act based on scenarios.
I'm having an issue with one of my controllers accessing the methods within a model.
I have a controller (application>controllers>event.php) with an index method:
class Event_Controller extends Base_Controller {
public $layout = 'layouts.default';
public $restful = true;
public function get_index()
{
$category = (isset($_GET['category']))? $_GET['category'] : NULL;
$date = (isset($_GET['date']))? $_GET['date'] : NULL;
$county = (isset($_GET['county']))? $_GET['county'] : NULL;
$events = Event::get_event_list($category, $date, $county);
$this->layout->title = 'Events';
$this->layout->nest('content', 'event.index', array(
//'data' => $some_data
));
}
}
And the model (application>models>event.php):
class Event extends Eloquent{
public static function get_event_list($category = null, $month = null, $county = null)
{
$events = DB::table('events');
if($month)
$events->where('dtDateTime', 'LIKE', $month.'-%');
if($category)
$events->where('strCategories', 'LIKE', '%['.$category.']%');
if($county)
$events->where('strCounty', 'LIKE', '%'.$tag.'%');
return $events->order_by('dtDateTime', 'DESC')->get();
}
}
If I change the model name in the call to the method in the controller (ie Eventsx::....) I get an error that the model doesn't exist so I know it can find the model, however when I try and run this (or with a call to any other method in the Model), I get the error:
Call to undefined method Laravel\Event::get_event_list()
I have similar controllers accessing similar methods in their models but for some reason, it is not playing ball here. The controller can access methods in other Models with no issues.
Here are my routes:
Route::get('/events', array('as' => 'event', 'uses' => 'Event#index'));
Route::get('/events/(:any)', array('as' => 'event', 'uses' => 'Event#event'));
Can anyone see anything glaringly obvious that I'm doing wrong here?
Thanks
I worked it out. class Event is already taken by laravel. I renamed my model and everything worked fine
<?php
class Home extends CI_Controller
{
public function __construct()
{
// load libraries //
$this->load->library('session');
$this->load->library('database');
$this->load->library('captcha');
// alternative
$this->load->library(array('session', 'database', 'captcha'));
// load models //
$this->load->model('menu_model', 'mmodel');
$this->load->model('user_model', 'umodel');
$this->load->model('admin_model', 'amodel');
// alternative
$this->load->model(array(?));
}
}
?>
How can i load all models in array? is it possible?
For models, you can do this:
$models = array(
'menu_model' => 'mmodel',
'user_model' => 'umodel',
'admin_model' => 'amodel',
);
foreach ($models as $file => $object_name)
{
$this->load->model($file, $object_name);
}
But as mentioned, you can create file application/core/MY_Loader.php and write your own method for loading models. I think this might work (not tested):
class MY_Loader extends CI_Loader {
function model($model, $name = '', $db_conn = FALSE)
{
if (is_array($model))
{
foreach ($model as $file => $object_name)
{
// Linear array was passed, be backwards compatible.
// CI already allows loading models as arrays, but does
// not accept the model name param, just the file name
if ( ! is_string($file))
{
$file = $object_name;
$object_name = NULL;
}
parent::model($file, $object_name);
}
return;
}
// Call the default method otherwise
parent::model($model, $name, $db_conn);
}
}
Usage with our variable from above:
$this->load->model($models);
You could also allow a separate DB connection to be passed in an array, but then you'd need to have a multidimensional array, and not the simple one we used. It's not too often you'll need to do that anyways.
I don't have any idea about the CodeIgniter 2.x but in CodeIgniter 3.x, this will also works :
$models = array(
'menu_model' => 'mmodel',
'user_model' => 'umodel',
'admin_model' => 'amodel',
);
$this->load->model($models);
Not natively, but you can easily extend Loader->model() to support that logic.
This work fine for me:
$this->load->model(array('menu_model'=>'menu','user_model'=>'user','admin_model'=>'admin'));