Using relations for setting in Zend_Db_Table_Row - zend-framework

is there a way how to use Zend_Db relations for setting related objects?
I am looking for something like following code:
$contentModel = new Content();
$categoryModel = new Category();
$category = $categoryModel->createRow();
$category->setName('Name Category 4');
$content = $contentModel->createRow();
$content->setTitle('Title 4');
$content->setCategory($category);
$content->save();
this provides small library:
http://code.google.com/p/zend-framework-orm/
does somebody have experience with that? Isn't there a plan for something similar in ZF ? Or is there something better for use? (I don't wnat to use doctrine ORM or something external)
thanks

I designed and implemented the table-relationships code in Zend Framework.
A foreign key ($content->category in your example) contains the value of the the primary key in the parent row it references. In your example, the $category doesn't contain a primary key value yet because you haven't saved it (assuming it uses an auto-incrementing pseudokey). You can't save the $content row until you populate its foreign key, so referential integrity is satisfied:
$contentModel = new Content();
$categoryModel = new Category();
$category = $categoryModel->createRow();
$category->setName('Name Category 4');
$content = $contentModel->createRow();
$content->setTitle('Title 4');
// saving populates the primary key field in the Row object
$category->save();
$content->setCategory($category->category_id);
$content->save();
It would do no good to pass the Row object to setCategory() if it doesn't have the primary key populated. $content->save() will fail if it doesn't have a valid primary key value to reference.
Since you need that primary key field to be populated in any case, it's not so difficult to access the field when you call setCategory().

I always override Zend_Db_Table and Zend_Db_Table_Row and use my own subclasses. In my Db_Table class I have:
protected $_rowClass = 'Db_Table_Row';
In my Db_Table_Row I have the following __get() and __set() functions:
public function __get($key)
{
$inflector = new Zend_Filter_Word_UnderscoreToCamelCase();
$method = 'get' . $inflector->filter($key);
if(method_exists($this, $method)) {
return $this->{$method}();
}
return parent::__get($key);
}
public function __set($key, $value)
{
$inflector = new Zend_Filter_Word_UnderscoreToCamelCase();
$method = 'set' . $inflector->filter($key);
if(method_exists($this, $method))
return $this->{$method}($value);
return parent::__set($key, $value);
}
Bascially that just tells the class to look for methods called getFoo() and setFoo() or whatever. You could then pretty much make up your own fields as long as your write your own logic behind. In you case maybe:
public function setCategory($value)
{
$this->category_id = $value->category_id;
}

Related

How to Use Automapper on DTO Returned From EF?

I was told to use automapper in the code below. I cannot get clarification for reasons that are too lengthy to go into. What object am I supposed to be mapping to what object? I don't see a "source" object, since the source is the database...
Would really appreciate any help on how to do this with automapper. Note, the actual fields are irrelevant, I need help with the general concept. I do understand how mapping works when mapping from one object to another.
public IQueryable<Object> ReturnDetailedSummaries(long orgId)
{
var summaries = from s in db.ReportSummaries
where s.OrganizationId == orgId
select new SummaryViewModel
{
Id = s.Id,
Name = s.Name,
AuditLocationId = s.AuditLocationId,
AuditLocationName = s.Location.Name,
CreatedOn = s.CreatedOn,
CreatedById = s.CreatedById,
CreatedByName = s.User.Name,
OfficeId = s.OfficeId,
OfficeName = s.Office.Name,
OrganizationId = s.OrganizationId,
OrganizationName = s.Organization.Name,
IsCompleted = s.IsCompleted,
isHidden = s.isHidden,
numberOfItemsInAuditLocations = s.numberOfItemsInAuditLocations,
numberOfLocationsScanned = s.numberOfLocationsScanned,
numberOfItemsScanned = s.numberOfItemsScanned,
numberofDiscrepanciesFound = s.numberofDiscrepanciesFound
};
return summaries;
}
It is a handy and a timesaver, especially if you use a one to one naming between translations layers. Here is how I use it.
For single item
public Domain.Data.User GetUserByUserName(string userName)
{
Mapper.CreateMap<User, Domain.Data.User>();
return (
from s in _dataContext.Users
where s.UserName==userName
select Mapper.Map<User, Domain.Data.User>(s)
).SingleOrDefault();
}
Multiple Items
public List<Domain.Data.User> GetUsersByProvider(int providerID)
{
Mapper.CreateMap<User, Domain.Data.User>();
return (
from s in _dataContext.Users
where s.ProviderID== providerID
select Mapper.Map<User, Domain.Data.User>(s)
).ToList();
}
It looks like you already have a model? SummaryViewModel?
If this isn't the DTO, then presumably you want to do:
Mapper.CreateMap<SummaryViewModel, SummaryViewModelDto>();
SummaryViewModelDto summaryViewModelDto =
Mapper.Map<SummaryViewModel, SummaryViewModelDto>(summaryViewModel);
AutoMapper will copy fields from one object to another, to save you having to do it all manually.
See https://github.com/AutoMapper/AutoMapper/wiki/Getting-started
The source is your entity class ReportSummary, the target is SummaryViewModel:
Mapper.CreateMap<ReportSummary, SummaryViewModel>();
The best way to use AutoMapper in combination with an IQueryable data source is through the Project.To API:
var summaries = db.ReportSummaries.Where(s => s.OrganizationId == orgId)
.Project().To<SummaryViewModel>();
Project.To translates the properties in the target model straight to the selected columns in the generated SQL.
Mapper.Map, on the other hand, only works on in-memory collections, so you can only use it when you first fetch complete ReportSummary objects from the database. (In this case there may not be much of a difference, but in other cases it can be substantial).

Zend Framework Adding Search Option

I am new to the Zend Framwork. I would like to add search function on the existing module. When the user enters the name in the textbox it should return the corresponding record. Could someone help me to add this function?
Thanks
PS: I don't want to use lucene
use this function:
define thes variable in your model
and add this function
protected $_name = 'your_table_name';// table name
public function getAll($order = null, $count = null, $offset = null) {
$rows = $this->fetchAll(NULL, $order, $count, $offset);
return $rows->toArray();
}

Zend DB relationships

I am creating an application in zend framework. Now i am stuck in the Zend table relationships.
I have 2 tables. I have set the primary key and the corresponding references in other tables.
Now i want to join the two tables using the relationships (not with the join functions). Is it possible in zend?
the tables structures are like the one below
Schemetable
scheme_id primary key
Scheme_name
Scheme_Desc
Ratestable
rate_id
Scheme_id *foreign key ref scheme_id*
rates:
Time periods:
There is an one to many relation b/w the scheme and rates
I have done some coding in the model classes
Scheme.php
class Scheme extends Zend_Db_Table_Abstract {
protected $_name = 'schemetable';
protected $_dependentTables = array('rates');
}
Rates.php
class Rates extends Zend_Db_Table_Abstract {
protected $_name = 'ratetable';
protected $_referenceMap = array(
'Scheme' => array(
'columns' => array('scheme_id'),
'refColumns' => array('scheme_id'),
'refTableClass' => 'Scheme',
),
);
}
How can i fetch every scheme and their corresponding rates?
Thanks in advance.
Please,
see the DOCS:
http://framework.zend.com/manual/en/zend.db.table.relationships.html
Fetching a Dependent Rowset
If you have a Row object as the result
of a query on a parent table, you can
fetch rows from dependent tables that
reference the current row. Use the
method:
$row->findDependentRowset($table);
Example #4 Fetching Dependent Rowsets
using the Magic Method
This example shows finding dependent
Rowsets equivalent to those in the
previous examples. In this case, the
application uses the magic method
invocation instead of specifying the
table and rule as strings.
$accountsTable = new Accounts();
$accountsRowset = $accountsTable->find(1234);
$user1234 = $accountsRowset->current();
// Use the default reference rule
$bugsReportedBy = $user1234->findBugs();
// Specify the reference rule
$bugsAssignedTo = $user1234->findBugsByEngineer();
Fetching a parent row
If you have a Row object as the result
of a query on a dependent table, you
can fetch the row in the parent to
which the dependent row refers. Use
the method:
$row->findParentRow($table);
This example shows finding parent Rows
equivalent to those in the previous
examples. In this case, the
application uses the magic method
invocation instead of specifying the
table and rule as strings.
$bugsTable = new Bugs();
$bugsRowset = $bugsTable->fetchAll(array('bug_status = ?', 'NEW'));
$bug1 = $bugsRowset->current();
// Use the default reference rule
$reporter = $bug1->findParentAccounts();
// Specify the reference rule
$engineer = $bug1->findParentAccountsByEngineer();

Updating a list of foreign keys in EF4, using MVC 2 Repository Viewmodel Pattern

Okay, I'm really struggling with how to update a list of foreign keys in MVC2/EF4.
I have a one to many relationship between a Template object which can have many or no TemplateScenario objects.
Essentially, I have an edit method in a controller that is trying to do this:
// POST: /Modes/Edit/1
[HttpPost]
public ActionResult Edit(int id, FormCollection formValues)
{
Template template = _templateRepository.GetTemplate(id);
TemplateCreateViewModel viewModel = new TemplateCreateViewModel();
viewModel.Template = template;
viewModel.TemplateScenarioList = template.TemplateScenarios.ToList();
//Update the model
UpdateModel(viewModel);
UpdateModel(viewModel.Template.TemplateScenarios, "TemplateScenarioList", new[] { "ScenarioID", "TemplateID" });
_templateRepository.Save();
return RedirectToAction("Edit", new { id = template.TemplateID });
}
This code successfully updates the 'template' object. It also adds the 'templatescenario' child objects BUT only if it is the first time I have added 'templatescenarios' to this particular template. If any templatescenario objects already exist for a given template, and I try to update them based on the new list, I get this error:
"The operation failed: The relationship could not be changed because one or more
of the foreign-key properties is non-nullable. When a change is made to a
relationship, the related foreign-key property is set to a null value. If the
foreign-key does not support null values, a new relationship must be defined,
the foreign-key property must be assigned another non-null value, or the
unrelated object must be deleted."
The _templateRepository.Save(); is just calling the entities.SaveChanges() EF4 method.
I can solve this in a dirty way by passing down a list of templatescenario ids to my repository class in a custom 'update' method that looks like this:
public void Update(Template template, IList<int> templateScenarios)
{
//Delete Old Entries
foreach (TemplateScenario ts in entities.TemplateScenarios)
{
if (ts.TemplateID == template.TemplateID)
{
if (templateScenarios == null)
entities.TemplateScenarios.DeleteObject(ts);
else if (!templateScenarios.Where(tsl => tsl == ts.ScenarioID).Any())
entities.TemplateScenarios.DeleteObject(ts);
}
}
//Don't need to add anything if they are null.
if (templateScenarios == null)
return;
//Add New Entries
foreach (int ts in templateScenarios)
{
if (!entities.TemplateScenarios.Where(tsc => tsc.ScenarioID == ts && tsc.TemplateID == template.TemplateID).Any())
{
TemplateScenario tempScenToAdd = new TemplateScenario();
tempScenToAdd.ScenarioID = ts;
tempScenToAdd.TemplateID = template.TemplateID;
entities.TemplateScenarios.AddObject(tempScenToAdd);
}
}
}
But that just feels dirty and I think I'm so close with the first, more automatic method. I've scoured the internet and found some similar posts on stackoverflow but am finding it difficult to reach that 'aha' moment.
Thanks,
Tom.
Incidently, I sorted out my problem.
The problem was my joining table was incorrectly using it's own primary key instead of using a composite key based on two foreign keys. This is obviously wrong /bad practice and EF4 and UpdateModel() don't play nice.
I had inherited the DB design from an ex-collegue and thus had taken the db design as correct without thinking too much about it. Very stupid of me, I know.

zend relationships with select

I am new to zend. I have been asked to redevelop a website that was once written in plain PHP and put it into the zend framework.
I am having a lot of trouble with database relationships, I cant seem to get my head round defining and querying relationships.
I would like to find a Category. From that Category I would like to be able to find all the CategoryInfo associated with it, and be able to query/sort/limit that dataset.
Here are my models.
Categorys.php
<?php
class Default_Model_Categorys extends Zend_Db_Table_Abstract
{
protected $_name = 'Categorys';
protected $_primary = 'id';
protected $_dependentTables = array('Default_Model_CategoryInfo');
}
?>
CategoryInfo.php
<?php
class Default_Model_CategoryInfo extends Zend_Db_Table_Abstract
{
protected $_name = 'Category_Info';
protected $_primary = 'id';
protected $_referenceMap = array(
'Categorys' => array(
'columns' => array('cat_id'),
'refTableClass' => 'Default_Model_Categorys',
'refColumns' => array('id')
)
);
}
?>
CategoryController.php
<?php
class CategorysController extends Zend_Controller_Action
{
public function indexAction()
{
/*
this should redirect to all games
*/
return $this->_forward("index", "games");
}
public function categoryAction()
{
/*
shows a specific category
*/
$id = (int) $this->_request->getParam('id');
$category = new Default_Model_Categorys();
$this->view->category = $category->fetchRow(
$category->select()->where('id = ?', $id)
);
$categoryInfo = $this->view->category->findDependentRowset('Default_Model_CategoryInfo');
}
}
Firstly... am I doing anything wrong?
Secondly... how do I go about querying the dependent rowset?
First, if you're searching for a category by its primary key, it's simpler to use the find() method:
$id = (int) $this->_request->getParam('id');
$category = new Default_Model_Categorys();
$this->view->category = $category->find($id)->current();
Second, to restrict or sort dependent Category_Info rows, you can use a Zend_Db_Table_Select object as an optional parameter of findDependentRowset(). Here's an example:
$select = $category->select()->where("info_type = 'PRICE'")
->order("info_date")
->limit(3);
$categoryInfo = $this->view->category->findDependentRowset(
'Default_Model_CategoryInfo', null, $select);
Notice you can use any table object to create that select object. Since the "FROM" clause for that select will be set by the findDependentRowset() method, you just add other clauses and then pass it in.
PS: You don't need to declare $_dependentTables at all, unless you're going to use cascading update or cascading delete via PHP code. I recommend strongly against doing that -- it's far more efficient to let the RDBMS handle those cascading operations.
Likewise you should never have to declare $_primary if your database tables actually declare primary key constraints. Zend_Db_Table_Abstract knows how to inspect metadata to get the primary key column(s).
Everything looks correctly to me. You don't query a dependent rowset. It is a query itself and it returns a result set. Basically what it is doing is pulling all records related to the current row you are working with as defined by $_referenceMap. Once you execute findDependentRowset(), you can foreach over the results which will give you instances of Zend_Db_Table_Row. From there you can display the related data as needed.
Personally I don't use Zend_Db Relationships. It is much easier to just make a second model method to query what I need. Also, Zend_Db Relationships do not support where clauses, so just making a second query is much more flexible than relationships.