everyone.
I was having issues with Easy Admin and the one-to-many relationship handled by Symfony. The three entities that I currently have in place are User, Products, and Category.
When I place the relationship on Product and Category, I get this error when I try to add a new Category in the admin section of Easy Bundle:
Catchable Fatal Error: Object of class AppBundle\Entity\Product could not be converted to string
Here are what my associates look like int the Product and Category classes, they are basically copied straight out of the Symfony docs.
The Product Category
/**
* #ORM\ManyToOne(targetEntity="Category", inversedBy="products")
* #ORM\JoinColumn(name="category_id", referencedColumnName="id")
*/
private $category;
And here is the association on the Category end:
...
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\OneToMany(targetEntity="Product", mappedBy="category")
*/
private $products;
public function __construct()
{
$this->products = new ArrayCollection();
}
The main issues that I believe is going on is that their is no field for category in the products table and this is leaving the Easy Bundle confused, since it seems to be relying on the class properties.
If anyone can make a suggestion on how to fix this, that would be great. Or if you know of a butter admin bundle to work with that might have this issues, that would also be great.
Also, if you have any experience with Easy Admin Bundle, do you suggest just developer just make their own. Because I do see other issue with this Bundle, such as being able to list category names on the Products new form, since Symfony only seems to log the category_id and not the name. I wouldn't be able to list the categories by name, just id numbers. And I would like to list of the different category names.
Any suggestions or help with this would be great.
I just got this error as well. Just add a __toString() magic method to your Product entity:
class Product {
...
public function __toString()
{
return $this->title; // <-- add here a real property which
} // describes your product
Related
I wrote a sql script and in it I created a table ;
Now I need to know ,how I can execute this script? (with which codes?)
And I have another question : where? where I must write this codes?(which folder in zend project?)
if it is possible for you please explain with an example.thanks
Creating tables in the database
Zend Framework is not supposed to be the one creating the tables, thus, my suggestion is to run those scripts in other environment.
The fastest one is, probably, the very own SQL shell, but you can use another software such as MySQLWorkbench if you are using MySQL.
Once the tables are created, the access to the tables is made this way:
Introduction
When you are using Zend Framework, you are making use of the MVC pattern. I suggest you to read what is that: Wikipedia MVC
If you read the Wikipedia link, you probably know now that the acess to the database is going to be made by the model.
Thus, if you followed the recommended project structure that Zend provides you will have a models folder under your application folder. There, you are supposed to implement the classes that will make access to the DB.
But well... you now know where to locate those classes but you will ask me: how? It's easy if you know where to search. ZF provides an abstract class called Zend_Db_Table_Abstract that has all the methods that will make your life easier talking about interaction with your database's tables. This is the class that your classes should implement.
Example
Let's suppose you've got a page in your website in which you want to show to the user a list of products of your local store. You have a table in your database called "products" in which you have all the useful information such us name, price and availability.
You will have a controller with an action called indexAction() or listAction() this action is prepared to send the data to your view and will look like:
class Store_ProductsController extends Zend_Controller_Action {
public function indexAction(){
//TODO: Get data from the DataBase into $products variable
$this->view->products = $products;
}
}
And your view file will that that products variable and do sutff with it.
But now comes the magic, you will have a class that will access to the database as I've said, it'll be like:
class Model_Store_Products extends Zend_Db_Table_Abstract{
protected $_name = 'products';
public function getAllProducts(){
$select = $this->$select()
->from(array('P'=>$this->_name),
array('id', 'name', 'price', availability));
$productsArray = $this->fetchAll($select);
return $productsArray;
}
}
And ta-da, you have your array of products ready to be used by the controller:
class Store_ProductsController extends Zend_Controller_Action {
public function indexAction(){
$model = new Model_Store_Products();
$products = $model->getAllProducts();
$this->view->products = $products;
}
}
It can be said that, since fetchAll is public function, and our select does basically nothing but set which columns do we want (it doesn't even have a where clause), in this case, it would be easier to call the fetchAll directly from the controller with no where and it will recover the whole table (all columns):
class Store_ProductsController extends Zend_Controller_Action {
public function indexAction(){
$model = new Model_Store_Products();
$products = $model->fetchAll();
$this->view->products = $products;
}
}
Thus, our function in the model is not even needed.
This is the basic information of how to access to the database using Zend Framework. Further information of how to create the Zend_Db_Table_Select object can be found here.
I hope this helps.
I'm in a situation where I have two entities in my database which I'm trying to convert to NOSQL. Site and Customer
A Customer has Sites
A Site can belong to a Customer but doesn't need one
I started thinking I would swap it around so I have a collection of Sites that can each have a customer but doesn't require one. But I still feel that Customer should be the parent as that seems more natural. However what would I do with sites without a customer in that case.
Is there a convention out there for dealing with this situation? I admit I'm still getting used to how things work in NOSQL
Thanks in advance.
If you use an object oriented database my suggestion would be to implement classes Site, Customer and SitePerCustomer like this
[Database]
public class Site
{
...
}
[Database]
public class Customer
{
public IEnumerable<Site> ConnectedSites
{
get
{
return SQL<Site>("SELECT s.Site from SitePerCustomer s WHERE s.ConnectedCustomer=?", this);
}
}
[Database]
public class SitePerCustomer
{
public Site ConnectedSite;
public Customer ConnectedCustomer;
}
Then you can use Customer for what it is and Site as well. The Customer instances can relate to all sites using a get property or method.
I've setup ZF2 Skeleton Application with Doctrine2. My goal is to create simple News service with simple hierarchy.
Category -> News -> Media (file)
I've setup all required relations for Category, News and Media (i.e. If News is deleted all related media is deleted from DB).
The problem is that media points to some file (located in file storage). I've implemented simple function that deletes all media related to News and then News it self.
$news->deleteImg();
$this->getEntityManager()->remove($news);
$this->getEntityManager()->flush();
It feels that this is wrong approach.
Is there a way to bind delete file function to Media Entity that will be called automatically each time Media is removed directly or throught it's parents? (i.e. News or Category)
Found the solution.
It's pretty simple:
First add annotation before Media class
/**
* Media
*
* #ORM\Table(name="media")
* #ORM\Entity
* #ORM\HasLifecycleCallbacks <- Add this line
*/
class Media
Then you need to add 2 functions for the class on PreRemove and PostRemove
/**
* #ORM\PreRemove()
*/
public function storeFilenameForRemove()
{
$this->temp = realpath($this->path);
}
/**
* #ORM\PostRemove()
*/
public function removeImg()
{
if (isset($this->temp)) {
unlink($this->temp);
}
}
This functions will be fired: 1st before remove (to store file name) and second after Entity is removed from DB to remove related File.
You need also to define
private $temp;
That stores file name.
That's it. Now when you remove news or news category all related media files will be removed with the it's entity.
I'm looking at using doctrine for an application I'm working on - but after reading the documentation I'm having trouble conceptualizing how to represent the database structure we have in terms of entities.
I have many tables which have partner tables which hold translation data like the following....
Where I would like to have one Entity (Navigation Element) which had access to the 'label' field depending on what Language I set in my application. The following from the Doctrine documentation seems to suggest that you need to define one (single) table which is used to persist an entity
http://www.doctrine-project.org/docs/orm/2.0/en/reference/basic-mapping.html
By default, the entity will be
persisted to a table with the same
name as the class name. In order to
change that, you can use the #Table
annotation as follows:
Or do I need to define two entities and link them (or allow the translation table to inherit from the element table).
And what strategy would I use to always insert a language_id clause to the Join (to ensure I'm pulling the right label for the currently set language). Is this something I would define in the entity itself, or elsewhere?
This seems to suit a One-To-Many Bidirectional association. This is the scenario from that page translated to your situation:
/** #Entity */
class NavigationElement
{
// ...
/**
* #OneToMany(targetEntity="NavigationElementTranslation", mappedBy="navigationElement")
*/
private $translations;
// ...
public function __construct() {
$this->translations = new \Doctrine\Common\Collections\ArrayCollection();
}
}
/** #Entity */
class NavigationElementTranslation
{
// ...
/**
* #ManyToOne(targetEntity="NavigationElement", inversedBy="translations")
* #JoinColumn(name="navigation_element_id", referencedColumnName="id")
*/
private $navigationElement;
// ...
}
You could add a getLabel($languageId) method to the NavigationElement entity that searches through the translations to get the correct label:
public function getLabel($languageId) {
foreach($this->translations as $trans) {
if($trans->languageId == $languageId)
return $trans->label;
}
throw new InvalidArgumentException();
}
And you could use the following DQL to ensure you only load the translation you want into the $translations property:
$query = $em->createQuery(
"SELECT ne, net
FROM Entity\NavigationElement ne
JOIN ne.translations net WITH net.languageId = :langId"
);
$query->setParameter('langId', $languageId);
$navigationElements = $query->execute();
This situation sounds like one where you would want to cache aggressively. Make sure you look into Doctrine 2's caching mechanisms too.
Also, internationalization can be handled reasonably well in PHP with gettext if you find join tables for translations start to become unmanageable.
I would also direct anyone who has to tackle this same problem to take a look at the following doctrine extension.
http://www.gediminasm.org/article/translatable-behavior-extension-for-doctrine-2
I started using NHibernate today, but I cannot figure out how I setup a simple relation between two tables. I don't really know what it's called, it could be one-to-many or foreign key relation (I'm not that into database design and the terms used), but here's a very simple example.
I have a table Product with attributes Id (PK), ProductName and CategoryId. Then I have a table Categories with attributes Id (PK) and CategoryName.
I created these classes:
public class Product
{
public virtual int Id { get; set; }
public virtual string ProductName { get; set; }
public virtual int CategoryId { get; set; }
public virtual Category Category { get; set; }
public virtual string CategoryName
{
get { return this.Category == null ? String.Empty : this.Category.CategoryName; }
}
}
public class Category
{
public virtual int Id { get; set; }
public virtual string CategoryName { get; set; }
}
In other words, I simply want the Product to store to which category it belongs (via the CategoryId attribute which points to an Id in the Categories table). I don't need the Category class to hold a list of related Products, if that makes it any simpler.
To make it even more clear what I'm after, this is the SQL that I'm expecting:
SELECT Products.*, Categories.*
FROM Products INNER JOIN Categories ON Products.CategoryId = Categories.Id
at least that's what I think it should be (again, I'm not that good at database design or queries).
I can't figure out which kind of mapping I need for this. I suppose I need to map it in the Product.hbm.xml file. But do I map the CategoryId as well? And how do I map the Category property?
It seems like I would need a 'one-to-many' relation since I have ONE category per product (or is this reasoning backward?) but it seems like there is no one-to-many mapping...
Thanks for any help!
Addition:
I tried to add the many-to-one relation in the Person mapping, but I keep getting an exception saying "Creating proxy failed", and in the inner exception "Ambiguous match found".
I should maybe mention I am using an old version of NHibernate (1.2 I think) because that is the only one I got running with MS Access due to it not finding the JetDriver in newer versions.
I've put the mapping files, classes, and code where the error occurs in screenshots because I can't figure out how to post XML code here... It keeps reading it as html tags and skipping half of it. Anyway.
The mappings:
http://www.nickthissen.nl/Images/tmp7B5A.png
The classes:
http://www.nickthissen.nl/Images/tmpF809.png
The loading code where the error occurs:
http://www.nickthissen.nl/Images/tmp46B6.png
(As I said, the inner exception says "Ambiguous match found".
(Product in my example has been replaced by Person)
The Person and Category classes inherit Entity which is an abstract base class and defines the Id, Deleted, CreatedTime and UpdatedTime properties.
The code where the error occurs is in a generic 'manager' class (type parameter TEntity which must inherit Entity). It is simply supposed to load all entities with the Deleted attribute false. In this case, TEntity is 'Person'.
It works fine if I leave out the many-to-one Category mapping in the Person mapping, but then obviously the Category property is always null.
Oh yeah, sorry about the mix between C# and VB, the C# code is in a generic framework I use for multiple projects while the VB part is the actual implementation of that framework on my website and I just happened to use VB for that.
Help? Thanks!
In your Product class only needs to contain a Category object, you don't need a CategoryId property. Then in your Product mapping you need to have this entry
<many-to-one name="Category" column="CategoryId" />
UPDATE:
Your mappings appear to be missing the fully qualified name of the mapped class in the tag. See http://nhibernate.info/doc/nh/en/index.html#mapping-declaration-class
UPDATE 2:
See if this helps you NHibernate 1.2 in a .NET 4.0 solution
The 'Ambiguous match found' exception was caused by the project targeting .NET Framework 4, which does not seem to be compatible with NHibernate 1.2.1. I switched to 3.5 and that seems to solve that particular issue.
Now on to the next. As you can see, the Person class has a CategoryName property that should return the name of the current Category object, or an empty string if the category happens to be null. This is so I can databind a collection of Person objects to a grid, specifying 'CategoryName' as a property to bind a column to.
Apparently this does not work with NHibernate. Whenever I try to databind my collection of persons, I get this exception:
"Property accessor 'CategoryName' on object 'NHibernateWebTest.Database.Person' threw the following exception:'Could not initialize proxy - the owning Session was closed.'"
This occurs on the 'DataBind' method call in this code:
public virtual void LoadGrid()
{
if (this.Grid == null) return;
this.Grid.DataSource = this.Manager.Load();
this.Grid.DataBind();
}
(This is an ASP.NET project and 'Grid' is a GridView)
'this.Manager' returns an existing instance of NHibernateEntityManager, and I've already shown its Load method before, it contains this:
public virtual EntityCollection Load()
{
using (ISession session = this.GetSession())
{
var entities = session
.CreateCriteria(typeof (TEntity))
.Add(Expression.Eq("Deleted", false))
.List();
return new EntityCollection(entities);
}
}
(THere's some generic type parameters in there but this website seems to hide them (due to the html like tags I guess)... Sorry about that).
This may have something to do with NHibernate itself, as I said I'm completely new to this. When I call my Load method I would expect it to return an EntityCollection(Of Person) with all its properties already set. It seems I have to keep the ISession open while I am databinding for some reason..? That seems a little strange...
Can I get around this? Can I make my Load method simply return a collection of persons already fully loaded, so that I can access CategoryName whenever I want?
Wait... Is this lazy loading perhaps?