Save data from a form collection - zend-form

I have been following http://framework.zend.com/manual/2.1/en/modules/zend.form.collections.html and it works great with validation and so on.
When the form is valid the guide just runs a var_dump on the entity and it looks something like this:
object(Application\Entity\Product)[622]
protected 'name' => string 'Chair' (length=5)
protected 'price' => string '25' (length=2)
protected 'categories' =>
array (size=2)
0 =>
object(Application\Entity\Category)[615]
protected 'name' => string 'Armchair' (length=8)
1 =>
object(App1ication\Entity\Category)[621]
protected 'name' => string 'Office' (length=6)
The categories can be more then 2 or just 1. How to save a normal form to a database table I understand and have no problem with. But here we have data for two different tables. I guess I could manually read the categories in my controller and fill them in to a model and save them row by row. But that doesn't feel like the best way of doing it.
How do I get the data from the entity to a model or my database? Can it be done without Doctrine?

You have two choices: getData() or bind().
bind() is the "automatic" way - you bind an entity to your form object which has a property on that entity which matches the name of your collection. Then, when the form's isValid() method is called, the binding mechanism will pass the values from the collection's elements to the matching property on the entity.
Alternatively, you can use getData() on the collection object and then do whatever you need to.
Once you have an entity, to save it, consider using ZfcBase as that does the hard work for you.
This is a simple example mapper:
namespace MyModule\Mapper;
use ZfcBase\Mapper\AbstractDbMapper;
use Zend\Stdlib\Hydrator\ArraySerializable;
use MyModule\Entity\MyEntity;
class MyMapper extends AbstractDbMapper
{
protected $tableName = 'my_table';
public function __construct()
{
$this->setHydrator(new ArraySerializable());
$this->setEntityPrototype(new MyEntity());
}
public function save(MyEntity $entity)
{
if (!$entity->getId()) {
$result = $this->insert($entity);
$entity->setId($result->getGeneratedValue());
} else {
$where = 'id = ' . (int)$entity->getId();
$this->update($entity, $where);
}
}
public function fetchAll($choiceGroupId)
{
$select = $this->getSelect($this->tableName);
return $this->select($select);
}
public function loadById($id)
{
$select = $this->getSelect($this->tableName)
->where(array('id' => (int)$id));
return $this->select($select)->current();
}
}
This mapper is using the ArraySerializable hydrator, so your entity object (MyEntity in the example) must implement the methods getArrayCopy() and populate(). getArrayCopy() returns an array of data to be saved and populate() is used to fill the entity from an array of data from database.

Related

Bug with my relation HasMany/BelongsTo

I have a model Work with this relation
public function types()
{
return $this->belongsTo('App\Models\Type');
}
And a model Type with this relation
public function works()
{
return $this->hasMany('App\Models\Work');
}
I try to access in my view show view to type but I've a lot of errors
Undefined property: Illuminate\Database\Eloquent\Relations\BelongsTo::$name
I try this : $work->types()->name for get data.
In my DB, my table 'Works' have a foreignkey 'type_id'.
I would like to get the 'type' of the post. There can be only one per post.
Thank you very much !
Semantically you want to make your relationships like so:
Work
// A work is of a single type
public function type()
{
return $this->belongsTo('App\Models\Type');
}
Type
// A type of work can have many items of work
public function works()
{
return $this->hasMany('App\Models\Work');
}
You can then access the relationship like so:
$type = Work::first()->type // return object of type Type
$works = Type::first()->works // return collection of objects of type Work
EDIT
By accessing the relationship with () you are returning the underlying query builder instance of the relationship and you will need to finish your statement with ->get() like so:
$works = Type::first()->works()->get();
You should have on Work Model:
public function type()
{
return $this->belongsTo('App\Models\Type');
}
and on your view:
$work->type->name;
Since you are not using default id as foreign key you should add
protected $primaryKey = "type_id";
in your model

LINQ query take(count) from collection of child elements

How can I make LINQ query (I am using Entity Framework) that returns top n elements in a child collection?
Here are example classes
public class A {
public int ID
public ICollection<B> bData
}
public class B {
public int ID
public string Name
}
This is kind of query I was thinking;
db.A.Where(a => a.ID == query_id).Include(a => a.bData.Take(count)).ToList();
Normally this doesn't work, how could I accomplish this?
The Include method is intended for eager loading the related entity data. It's all or nothing and cannot be used for filtering, sorting, grouping etc.
So what are you asking for can be accomplished with projection (select). It would be simpler if you project to anonymous or custom type (a.k.a. DTO object), because EF does not allow projecting to entity type. But it's still doable by using anonymous type projection in LINQ to Entities query and doing second projection in LINQ to Objects to attach the filtered child collection to its parent entity like this:
var result = db.A
.Where(a => a.ID == query_id)
.Select(a => new { a, bData = a.bData.Take(count).ToList() })
.AsEnumerable() // Switch to LINQ to Object context
.Select(x =>
{
x.a.bData = x.bData;
return x.a;
})
.ToList();
Please note that in order the above to work, bData member should not be virtual or db.Configuration.LazyLoadingEnabled should be false (i.e. lazy loading should be off, otherwise the bData collections will be fully reloaded when first accessed).

QueryOver IList<string> property

I have a mapped class that has a ICollection property which is mapped as a Set (using by code mappings). Note that the collection contains strings and not another mapped entity. e.g.
public class Item
{
public virtual ICollection<string> Facts { get; set; }
}
public class ItemMapping
{
public ItemMapping()
{
Set(x => x.Facts, m =>
{
m.Key(k => k.Column("ItemId"));
m.Table("Facts");
}, col => col.Element(m =>
{
m.Column("Description");
m.Type(NHibernateUtil.String);
}));
}
}
This works and CRUD operations on Items with Facts works fine.
However, I want to QueryOver<> the Facts in the database (e.g. retrieve the count or first 20 facts or retrieve some random facts) but given there is no entity how do I do this? I don't want to introduce a Fact entity because the only property it would have would be a string.
Well, my suggestion would be:
introduce entity. Even if it would have only one property. Later you can extend that (with Order, IsVisible). And if you will do that everywhere your framework will be built only from one-to-many and many-to-one relations among first citizens objects. That mean simple "framework generalization, reuse..."
But I see that you do not like it (please, at least try to re-think) - so there is the way:
NHibernate How do I query against an IList property?
Where I tried to show that (based on the documentation) we can use magical word ".elements":
17.1.4.1. Alias and property references
So the query which will touch the string elements in your case
Item item = null;
string fact = null;
var demos = session.QueryOver<Item>(() => item)
.JoinAlias(i => i.Facts, () => fact)
// instead of this
// .Add(Restrictions.Eq("fact", "abc"))
// we can use the .elements keyword
.Where(Restrictions.Eq("fact.elements", "abc"))
.List<Item>();
So, this way, you can get Items which do have some facts equal to "abc"
non entities have to be queried by their entities and selected then. For example to get the first 20 facts:
string fact = null;
var first20facts = session.QueryOver<Item>()
.JoinAlias(i => i.Facts, () => fact)
.OrderBy(() => fact).Asc
.Take(20)
.Select(() => fact)
.List<string>();
Alternativly you could also map a readonly entity for Fact just to query it.

YII: Validate does not populate model unless there is a rule?

What am I missing here. I've got a model with a bunch of variables:
class Car extends CFormModel
{
public $item1;
public $item2;
}
If I post the form with item1 = "one" and item2 = "two" and I do the following in the controller:
if(isset($_POST['Car']))
{
$model->attributes = $_POST['Car'];
if($model->validate()) {
print_r($model);
...
...
At the point where I print the model, none of the items have values. But I add this to my model:
public function rules()
{
return array( array('item1', 'required'));
}
Then item1 populates, but not item2. How then do you get the values for OPTIONAL fields?
If you do not assign any validation rules to property then this property is "unsafe". If property is unsafe then you cannot mass assign anything there, however you can directly assign the variable.
$model->item1 = $_POST{'car']['item1'];
If you are trying to mass assign variables you should also see a warning in Yii trace log.
To read property, read it directly from property
var_dump($model->item1);

Trying to make models in Kohana, relations problem

I have a table of Hits, Articles and Categories
Now, a Hit belongs_to an Article/Category (depends on where it was done).
so I have a column on Hits table with the name 'parenttype'
That tells me 'Article' or 'Category'.
I wrote in the Hit model (extends ORM)
protected $_belongs_to= array(
'page' => array('model'=> $this->parenttype)
);
Now it complains about $this->parenttype not being expected?
you should declare the variable protected $_belongs_to = NULL;
and on the constructor set it's value after calling the parent class constructor
public function __construct() {
parent::__construct();
$this->_belongs_to = array('page' => array('model' => $this->parenttype));
}
How do you intend to access $this if the object is just about to be instantiated?
( even if you could, $this->parenttype definitely hasn't been loaded before relations were )
This means you need to define that relation some other way, a little bit later :)
( I still don't like the way you're doing it )