I have recently been playing with some very general OOP Concepts and learning to abstract my code. For this purpose I wrote a general car api that saves data to an array.
The problem is I can add, list, delete and find the data but when it comes to editing....well that's difficult.
I wrote a find method that would do:
public function find($car){
if(in_array($car, $this->_carArray)){
return $this->_carArray[$car];
}
return false;
}
So now I can find and return the car object. Which is just an array such as:
array(
'type' => 'gas',
'make' => 'some-make'
'model' => 'some-model',
'year' => '2005'
);
Then I can use a custom __set() method I wrote to set say a new type, or a new model or even add an odometerReading.
The problem is, when I do all that, I am left with an array - How do I save that data using a save method?
I hope I gave enough info.
Abstracting away from objects is not an OOP concept. If you want to abstract the persistence then the Repository should be handling the marshalling/unmarshalling and the client code should be dealing with objects
Related
Issue: multiple fieldsets in form are not populated/hydrated when using $form->bind($object). How do you populate 2 different fieldsets in a form vai 2 different entity objects?
I have 2 fieldsets: FieldsetA, FieldsetB
A form RegisterFrom calls these in its init() method
class RegisterForm extends Form
{
public function init(){
$this->add(array(
'name' => 'service_provider_fieldset',
'type' => ServiceProviderFieldset::class, // this is one model/entity
));
$this->add(array(
'name' => 'location_fieldset',
'type' => LocationFieldset::class, // this is a separate model/entity
));
}
}
Creating the fieldsets: (note commented out attempts at hydration)
class ServiceProviderFieldset extends Fieldset
{
public function init(){
//parent::__construct($name);
/*
$this
->setHydrator(new ClassMethodsHydrator(false))
->setObject(new ServiceProvider())
;
*/
/*
$this
->setHydrator(new ReflectionHydrator(false))
->setObject(new ServiceProvider())
;
*/
$this->add(array(
'type'=>'Hidden',
'name'=>'id',
'options'=>array(
'label' => 'Service Provider Id'
),
'attributes'=>array(
'id'=>'providerId'
)
));
}
}
Controller:
$provider = $this->findServiceProviderById($providerId); // this is set from DB call and correctly creates a Provider() object with populated values.
$location = $this->findServiceProviderLocationById($locId);
$form = $formManager->get(RegisterForm::class);
$form->bind($provider);
$form->bin($location);
// $form->get('service_provider_fieldset')->bindValues(...);
View:
$formElement = $form->get('service_provider_fieldset')->get('email');
etc...
The form renders in the view correctly BUT without the populated data.
NOTE: NOT using Doctrine but I retrieve the data from the DB OK.
NOTE: IF I set this flag 'use_as_base_fieldset' => true, then 1 of the Objects (ServiceProvider) populates, visa-versa if I set the location fields to 'true' then that populates.
I've been searching for a couple of hours, trial and error with no success and I'm hoping it's just my fatigue which has missed a simple setup/config step to get this to work.
Summary: How do you populate 2 or more Fieldsets with 2 or more entities within a form?
Bind(), fieldset->bindValues()?,
Tried:
$form->get('service_provider_fieldset')->allowObjectBinding(true);
$form->get('service_provider_fieldset')->allowedObjectBindingClass(\Provider\Form\ServiceProviderFieldset::class);
These are some links that are close but still cannot populate both field sets via separate entities.
ZF2 Form Hydration with multiple objects and fieldsets
https://framework.zend.com/manual/2.4/en/modules/zend.form.collections.html
hydrating multiple objects from fieldsets ZF2
The collections (product/brand/category) example implies a 'single' collection using the 'use_as_base_fieldset' => true, is used to bind()...?
On your webpage, check the form's elements names that relate to your fieldsets. They should be something like this: yourFieldsetName[yourElementName]. If you just see yourElementName, that most likely means that forgot to prepare() your form in the view script.
This is exactly what happened to me, and after I prepare()ed the form, all the objects got hydrated without a problem.
UPDATE = answer to comment's questions: Not resolved as such. Is this bad design? Note: I am using prepare() on in the view.
If everything works fine, your 2 objects should hydrate. use_as_base_fieldset flag is used for basically saying, 'hey that's me (the fieldset) you should only hydrate object with data/extract data from object'. So what you get with one object being hydrated and the other not, and vice versa is predictable. It's quite difficult to say what's going wrong without looking at your complete code. I'm afraid posting too much will also take time for the answerer's to grasp, and my experience is that such questions are usually left unanswered. What I usually do in situations like yours is that I go step by step in the Zend Form's and Fieldsets methods used in hydration/extraction. I use \Zend\Debug\Debug::dump($somethingThatYouWantToCheck); die();. That's not the best method I presume, but it works.
If I were you, I would also do the following.
From your post, it's not clear why you use form's init() method. The init() method is used when you want, for example, some elements in your form be filled from DB (like <select>). The Form runs init() method when some things that aren't available in in the __construct() method yet, but only after the instance of the form is created (not 100% sure about this, double-check this).
Don't worry about good/bad design. Design is a very good thing, but if you have a small or middle system, the design considerations won't affect the performance/complexity of the system. But rather you'll spend really a lot of time doing everything right than just doing it and if it works ok, forgetting about it.
If you don't wanna go with \Zend\Debug\Debug::dump($somethingThatYouWantToCheck); die(); (that could be quite tedious, I know), create one fieldset and attach to it your desired 2 fieldsets. Then include this fieldset in the form and use use_as_base_fieldset = true on this fieldset (of course you'll also need to create object corresponding to this fieldset with 2 nested objects that are attached to your current fieldsets, and attach the object to the fieldset).
Hope this helps at least a little.
To work with several objects you need:
1. Create a form with fieldset for each object as you have done.
You have to specify a name for each fieldset (e.g. in constructor).
2. In each fieldset we need to specify hydrator
e.g.: $this->setHydrator(new ClassMethods());
Zend\Hydrator\ClassMethods for using getter functions or
Zend\Hydrator\ArraySerializable for using getArrayCopy method.
and allow object class:
$this->setAllowedObjectBindingClass(YourClassObject::class);
You can do it in init method in fieldset.
3. Set hydrator for main form:
$this->setHydrator(new ArraySerializable());
4. Now in controller method you can create object of Zend\Stdlib\ArrayObject:
$obj = new ArrayObject();
then add your objects with a key equals fieldset name:
$obj->offsetSet("fieldset_name", $your_object);
and then you can bind $obj to your form:
$form->bind($obj);
I hope this helps. And don't forget about prepare method:
return new ViewModel(["form" => $form->prepare()]);
I'm creating a portfolio page in Codeigniter, and I'm also equipping the site with a simple CMS.
I have a controller that creates portfolio items as follows:
public function create_portfolio()
{
$this->load->model('portfolio_model');
$this->portfolio_model->insert(
$this->input->post('title'),
$this->input->post('short_description'),
$this->input->post('complete_description'),
$this->input->post('github'),
$this->input->post('url')
);
}
An object-oriented approach would be something like this:
public function create_portfolio()
{
$this->confirm_login();
$this->load->model('portfolio_model');
$portfolio = new Portfolio(
$this->input->post('title'),
$this->input->post('short_description'),
$this->input->post('complete_description'),
$this->input->post('github'),
$this->input->post('url')
);
$this->portfolio_model->insert($portfolio);
}
With employers increasingly seeking those with OOP skills, I'm trying to evaluate whether the latter approach is effective. In the end, the model that I have would end up accessing all of the instance variables from the Portfolio object in order to insert into the database (I am not using an ORM).
Is there any point to actually grouping all of the input fields into an object before passing it onto the model? I may be phrasing this question the wrong way, but I would love your input.
Since your are passing values to a constructor, the constructor may do validation on the input to ensure it is valid or do some sort of other processing where
$portfolio->title may not equal $this->input->post('title');
So option 2 would be better.
I'm looking to use Lithium framework to build my application config interface as I like its minimal approach and the document-store (i.e. Mongodb) centric model.
However, (and I know its not quite released yet), there is little-to-no information, tutorials or examples out there to move you on from the simple blog tutorial.
What I am trying to do now is build an app that will show me the collections I have in Mongodb, and then let me work with which ever collection I choose. I can't seem to figure out:
a) how would I build a model that enumerates the collections - preferably according to my internal naming scheme,
b) how do I break the convention model so I can specify the name of the collection to use?
I think there are two things i'm struggling with to answer these two questions - perhaps a fundamental misunderstanding of how to move a model in MVC beyond the simple collection-model-controller-view examples, and secondly, the actual process of telling the mongo datasource what collection to use.
any pointers or examples, gratefully received.
Chris
update::
So I figured out how to set the collection - for reference you can set source in the $_meta array like this:
protected $_meta = array(
'source' => '<<collectionName>>'
);
still no idea how to use a Model that will list me all the collections I have in my DB though. Any ideas how to do that from a philosophical and also technological manner?
further update::
so I have got a bit further thanks to the comments below. At least I might now be able to re-phrase the question a bit. I can define my model something like this:
<?php
namespace app\models;
use lithium\data\Model;
class Posts extends \lithium\data\Model{
protected $_meta = array('source' => false);
public function testcolls(){
return (self::connection()->sources());
}
}
?>
then in my view I can use:
<?php foreach ($post->testcolls() as $coll): ?>
<h2><?=$coll ?></h2>
<?php endforeach; ?>
that works - however, what I really want to do is not create a 'testcolls' method in my Model but as Medhi suggested below, I need to override the find method. I can't seem to figure out how to do that and what it would need to return. The docs are not too clear on this.
final update
based on the comment below and a bit of experimentation, I came up with the following that works for being able to call find with a collection as a parameter.
model:
class Dataqueues extends \lithium\data\Model{
protected $_meta = array('source' => false);
public static function find($filter, array $options = array()) {
if (isset($options['collection'])){
self::meta('source', $options['collection']);
}
return parent::find('all',$options);
}
}
controller:
class DataqueuesController extends \lithium\action\Controller {
public function index() {
$dataqueues = Dataqueues::find('all',array('limit'=>20,'collection'=>'W501'));
return compact('dataqueues');
}
}
getting a model that returns a list of collections was also pretty simple in the end:
class Collections extends \lithium\data\Model{
protected $_meta = array('source' => false);
public static function find($filter, array $options = array()) {
return self::connection()->sources();
}
}
note that the controller won't support options or filters.
Nothing holds you from having a Collections Model, where you set $_meta['source'] = false to prevent Lithium from looking for a Collection in your database named collections.
In this model, you can call YourModel::connection()->sources() to list all your Mongo Collections.
Docs for sources(): http://li3.me/docs/lithium/data/source/MongoDb::sources(). Basically it calls listCollections() on a MongoDB instance http://php.net/manual/en/mongodb.listcollections.php
You can override your Model::find() method to return the list of collections, instead the list of documents, or pass the collection as a param Collections::find('all', array('conditions' => ..., 'collection' => 'foo'))... or wathever you want :-)
Lithium is designed to don't force that much on you !
First of all, Lithium follows the convention over configuration approach.
What this means:
Configuration: 'source' => '<< collectionName >>'
Convention: Name your model and your collection the same thing, the framework handles the rest.
IE: A "People" collection will have a "People" model
Second, connect to your database:
Configure your connections.php file in app\bootstrap\connections.php. I know I said convention over configuration, but you still need to let the framework know where the database is and what the login info is. For details look at the http://li3.me/docs/manual/quickstart. Here is the relevant code:
// MongoDB Connection
Connections::add('default', array('type' => 'MongoDb', 'database' => 'blog', 'host' => 'localhost'));
Third, get data
Create a model, matching your collection name, and then in your controller, add this line:
$model = Models::all();
where model is the singular name for what you are storing in your collection, and Models is the name of your model. That is it.
If you put a break point after this line, you will see your Models collection. For more information, see http://li3.me/docs/manual/working-with-data/using-models.wiki
Finally, to pass it to your view, simply put this line of code at the end of your controller:
return compact('model', 'model2', 'model3');
where model would be what you just pulled in the third step. The models 2 and 3 that I tacked on is how you would pass any other collections you pulled.
In your view, you would just reference $model to and assume that the relevant fields are there. You don't have to worry about putting getters or setters or anything else like that.
For example: if you want to show the data in $model:
foreach ($model as $aModel) {
echo $aModel;
}
See Accessing View Variables in: http://li3.me/docs/manual/handling-http-requests/views.wiki
Hope this helps.
I have recently begun working on a PHP/JS Form Class that will also include a SQL Form builder (eg. building simple forms from sql and auto inserts/updates).
I have tried several classes (zend_form, clonefish, PHP Form Builder Class, phorms etc) but as yet haven't come across a complete solution that is simple, customizable and complete (both server side and client side validation, covers all simple html elements and lots of dhtml elements: sorting, wysiwyg, mutli file upload, date picker, ajax validation etc)
My question is why do some "classes" implement elements via an array and others via proper OO class calls.
eg.
Clonefish (popular commercial php class):
$config = Array(
'username' => Array(
'type' => 'inputText',
'displayname' => 'Username',
validation => Array(
Array(
'type' => 'string',
'minimum' => 5,
'maximum' => 15,
),
),
));
$clonefish = new clonefish( 'loginform', 'test.php', 'POST' );
$clonefish->addElements( $config, $_POST );
Then others eg. Zend_Form
$form = new Zend_Form;
$username = new Zend_Form_Element_Text('username');
$username->addValidator(new Zend_Validate_Alnum());
$form->addElement($username);
I realise Zend_Form can pass elements in via an array similar to clonefish but why do this?
Is there any benefit? It seems to make things more complicated especially when using a proper IDE like Komodo.
Any thoughts would be appreciated as I dont want to get too far down the track and realize there was great benefit in using arrays to add elements (although this wouldn't be much of a task to add on).
Cheers
My question is why do some "classes" implement elements via an array and others via proper OO class calls.
For convenience. It's less verbose and it feels less like coding and more like configuration and you need less intimate knowledge of the API.
Btw, the reason you have not yet come across a complete solution that is simple, customizable and complete is because it is not simple. Forms, their validation and rendering is complex, especially if you want to have it customizable for any purpose. ZF's form components are a good example of how to properly decouple and separate all concerns to get the ultimate extensible form builder (including client side code through Zend_Dojo or ZendX_Jquery). But they are also a great example of the complexity required for this. Even with the convenient array configuration, it is damn difficult to make them bend to your will, especially if you need to depart from the default configuration and rendering.
Why to use objects? Becouase they are a much more complex types. Consider the following example (I never useed Zend_Form so I don't even know its architecture):
class MySuperAlnumValidator extends Zend_Validate_Alnum {
protected $forbiddenWords = array();
public function addForbiddenWord($word) {
$this->forbiddenWords[] = $word;
}
// Override Zend_Value_Alnum::validate() - I don't know whether such a method even exists
// but you know what's the point
public function validate() {
parent::validate();
if (in_array($this->value, $this->forbiddenWords) {
throw new Exception('Invalid value.');
}
return $this->value;
}
}
// -----------------------
$validator = new MySuperAlnumValidator();
$validator->addForbiddenWord('admin');
$validator->addForbiddenWord('administrator');
$username->addValidator($validator);
This is only a simple example but when you start writing more complex validators/form fields/etc. then objects are, in principle, the only meaningful tool.
I want to save an object or form to the database. Only I can't find the easiest (or normal) way for how to do this.
I found a lot of tutorials, but none seem to be easy or current. Can someone please help me with this?
I use version 1.9.3 of the Zend Framework.
The easiest way (aka the way using the smallest amount of code) to insert a row into a database table using Zend_Db is:
$data = array(
'created_on' => '2007-03-22',
'bug_description' => 'Something wrong',
'bug_status' => 'NEW'
);
$db->insert('bugs', $data);
The above code will insert a new row into the bugs table whereas $db is the Zend_Db_Adapter_Abstract-subclass you created with Zend_Db::factory(). Please see Writing Changes to the Database in the Zend Framework manual for more details and the whole spectrum of features Zend_Db provides.
For the sake of completeness, the above code will issue a query to the database similar to:
INSERT INTO bugs (created_on, bug_description, bug_status)
VALUES ('2007-03-22', 'Something wrong', 'NEW')
The next step would be a more sophisticated approach using Zend_Db_Table.
EDIT:
Given that you have a Zend_Form ($form) with the appropriate fields created_on, bug_description and bug_status and provided that you have the right filters and validators in place, adding a new row with values given in the form is as easy as
if ($form->isValid($_POST)) {
$db->insert('bugs', $form->getValues());
}
Storing a custom object is also very easy:
// $bug is your custom object representing a bug
$db->insert('bugs', array(
'created_on' => $bug->getCreatedOn(),
'bug_description' => $bug->getDescription(),
'bug_status' => $bug->getStatus()
));
Instantiate any object that you need and serialize it. Once serialized, you can store it or transmit it to pretty much any medium. Is this what you are referring to?