Symfony relationship get by id in Entity without repository - php

I have in my Item ORM entity a relationship with Packs.
Now in my code I have a PackId and I have an Item. Now here's what I am trying in my code:
$item->getPackById($pack->getId);
In my Item Entity I try something like this:
public function getPackById(UuidInterface $packId)
{
return $this->packs[$packId];
}
I am not sure If I can do this without a repository or is there a better way?

Normally we try to avoid to much work in an entity but as you are certain that the packs UUID are unique you could try something with an array filter like :
public function getPackById(Uuid $uuid): ?Pack
{
$matching = array_filter((array)$this->packs, fn(Pack $pack) => $pack->getUuid() === $uuid);
return $matching[0];
}

Related

How to set doctrine associations?

I know that association property in entity is implements \Doctrine\Common\Collections\Collection. I know that in constructor such properties should be initialized:
$this->collection = new \Doctrine\Common\Collections\ArrayCollection()
I know that I can modify collections using ArrayCollection#add() and ArrayCollection#remove(). However I have a different case.
Suppose I have a new simple array of associative entities. Using existing methods I need to check every element in array: if entity collection has it. If no - add array element to entity collection. In addition to this, I need to check every element in entity collection. If any collection element is absent in new array, then I need to remove it from collection. So much work to do trivial thing.
What I want? To have the setProducts method implemented:
class Entity {
private $products;
// ... constructor
public function setProducts(array $products)
{
// synchronize $products with $this->products
}
}
I tried: $this->products = new ArrayCollection($products). However this makes doctrine remove all products and add those ones from $products parameter. I want similar result but without database queries.
Is there any built in solution in Doctrine for such case?
Edit:
I would like to have a method in ArrayCollection like fromArray which would merge elements in collections removing unneeded. This would just duplicate using add/remove calls for each element in collection argumen manually.
Doctrine collections do not have a "merge"-feature that will add/remove entities from an array or Collection in another Collection.
If you want to "simplify" the manual merge process you describe using add/remove, you could use array_merge assuming both arrays are not numeric, but instead have some kind of unique key, e.g. the entity's spl_object_hash:
public function setProducts(array $products)
{
$this->products = new ArrayCollection(
array_merge(
array_combine(
array_map('spl_object_hash', $this->products->toArray()),
$this->products->toArray()
),
array_combine(
array_map('spl_object_hash', $products),
$products->toArray()
)
)
);
}
You might want to use the product id instead of spl_object_hash as 2 products with the same id, but created as separate entities - e.g. one through findBy() in Doctrine and one manually created with new Product() - will be recognized as 2 distinct products and might cause another insert-attempt.
Since you replace the original PersistentCollection holding your previously fetched products with a new ArrayCollection this might still result in unneeded queries or yield unexpected results when flushing the EntityManager, though. Not to mention, that this approach might be harder to read than explicitly calling addElement/removeElement on the original Collection instead.
I would approach it by creating my own collection class that extends Doctrine array collection class:
use Doctrine\Common\Collections\ArrayCollection;
class ProductCollection extends ArrayCollection
{
}
In the entity itself you would initialise it in the __constructor:
public function __construct()
{
$this->products = new ProductCollection();
}
Here, Doctrine will you use your collection class for product results. After this you could add your own function to deal with your special merge, perhaps something:
public function mergeProducts(ProductCollection $products): ProductCollection
{
$result = new ProductCollection();
foreach($products as $product) {
$add = true;
foreach($this->getIterator() as $p) {
if($product->getId() === $p->getId()) {
$result->add($product);
$add = false;
}
}
if($add) {
$result->add($product);
}
}
return $result;
}
It will return a brand new product collection, that you can replace your other collection in the entity. However, if the entity is attached and under doctrine control, this will render SQL at the other end, if you want to play with the entity without risking database updates you need to detach the entity:
$entityManager->detach($productEntity);
Hopes this helps

laravel orm: different result from almost same query

I've a very big doubt about how works laravel for a very simple thing:
If I call:
$companies=User::All();
Then I can use statement like this in a forach:
foreach($companies as $company)
$company['new_field']= 'something';
If i'm limiting the output of the query like:
$companies = DB::table('companies')
->select('id','name','email','business_name',...)->get();
The things doesnt work as before,
I try with or without the ->get()
I try to convert with ->toArray() (errors rised)
I try with put() and push() for collections method and agains errors...
How can I add a field in every item of the collection just to pass it to a view?
Try like this, hope it works for you:
$users=User::select('id','name','email','business_name',...)->get()->toArray();
and then use foreach loop like this:
foreach($users as $key => $value ){
$users[$key]['newField'] = "Demo";
}
If you are using Laravel and model in it so there is a better way to add custom attribute or field here is what i do for custom field
For Example :
There is a Model Name User
so in User Model
add a property name appends like :
class User extends Model
{
protected $appends = ['new_field'];
public function getNewFieldAttribute() // defining field logic here
{
return // your code
}
So you no need to use foreach and looping and adding new field
for more have a look on laravel doc : https://laravel.com/docs/5.5/eloquent-mutators#accessors-and-mutators
Suggestion
you can limit your output with Model too.
User::select('id','name','email','business_name',...)->get();
if you are making an array like
User::select('id','name','email','business_name',...)->get()->toArray();
so this will also give you your custom field

Laravel's Eloquent table 'inheritence' with parents

I have a Laravel model acl_groups that has a JSON column inherits. What should I do, the "laravel way" to query the inherited groups when checking if a group can do something? The rights are stored in another JSON column, allow/deny so I can just do a in_array to check a single group if they have access.
On your model you can set a getter
public function getInheritsAttribute($v)
{
return $v ? json_decode($v, true) : [];
}
OR
if you dont want a getter you can try a pseudo getter
public function getPseudoAttribute()
{
return $this->inherits ? json_decode($this->inherits, true) : [];
}
Kind of maybe did mistake on second one.
And on other model the same thing
so when you call $item->inherits = you will get an array
First you may try to prepare the array like removing same keys or values
and after just check
if (array_key_exists('thing_to_check', $item->inherits)) {
return true;
}
This is not a working code, it is just an idea how you can do you.
Take a look at Cartalyst Sentinel how they check the permissions for groups and users.

Mapping myself form in Symfony2

I'd like to map an entity of a form by myself. The thing is I'm working with 36 000 cities in a database and Doctrine doesn't return any result when I'm performing a request using findBy. But I set this up by writting my owns methods.
The problem is in a form I need to ask for a city through an entity field (because there are a lot of data, I'm using select2 with remote's data). So far, no problem but when I'm submiting the form, Symfony can't bind the city's id to a database entry because of the none result of the classic method of Doctrine.
So, my question is : How can I tell Symfony to use my repository's method instead of the Doctrine's one to bind my data?
Thank you very much ! And have a good day ;)
The method findBy() request an array parameter sometimes people miss that:
$result = $this->getDoctrine()->getManager()
->getRepository("AcmeDemoBundle")->findBy(array(
"city" => $city)
);
If you want to use repository you just need to map it to your class:
/**
* #ORM\Entity(repositoryClass="Acme/DemoBundle/Repository/CountryRepository")
*/
class Country
{ ... }
Then in
class CountryRepository extends EntityRepository
{
public function getMySpecificCity($city)
{
$qb = $this->createQueryBuilder('c');
$cities = $qb->select(*)
->where("c.city =:city ")->setParameter('city', $city)
->getQuery()
->getResult();
return $cities;
}
...
}
So you can use it as follow:
$result = $this->getDoctrine()->getManager()
->getRepository("AcmeDemoBundle")->getMySpecificCity($city);

I would like to know if an object is in my Doctrine Collection

I'm working on Symfony 1.4 with Doctrine 1.2 and I have some problems.
I have created one Doctrine Collection of my Products like this :
$oProductCollection = new Doctrine_Collection('Products');
And I add some product in :
$oProductCollection->add($oMyProduct);
Then I want to know if a product is already in my Collection. Because if I add my product twice, that overwrite my old version...
I found "contains" function but I can't give my product object directly and I don't know what the key is...
Could you help me please ?
You can set the keyColumn by
//set the id column as key
$oProductCollection = new Doctrine_Collection('Products', 'id');
Then you can use $oProductCollection->contains($oMyProduct->getId()); to check whether $oMyProduct is already in your Collection.
Now you are able to write
if ($oProductCollection->contains($oMyProduct)){
echo "Its already in";
}else{
$oProductCollection->add($oMyProduct);
}
Another alternative. Index your collection by id, and just check if it exists. It should be pretty fast. Take a look at the docs.
Something like:
$id = $oMyProduct->getId();
if (!empty($oProductCollection[$id])){
...
}
You should implement a method Produits::equals(Produit $p) check every object of the collection using a loop.
foreach ($oListeProduit as $p) {
if ($p->equals($produit)) {
return true;
}
}
return false;
You have to use the second parameter of the Doctrine_Collection constructor:
public function __construct($table, $keyColumn = null)
So:
$oProductCollection = new Doctrine_Collection('Products', 'id');
And then contains with an id will work.
Edit: grilled :(

Categories