Parse: How do I create a Pointer data with PHP SDK? - php

The examples shown here (https://parse.com/docs/relations_guide#onetomany-pointers) is in iOS and Android.
Is it possible to create a Pointer data store with PHP SDK?

I just ran into the same issue trying to make a many-to-many relation and after some trial and error I got it working by doing:
$obj = new ParseObject("ObjectClassName");
$obj->setAssociativeArray("relationFieldName", array('__type' => 'Pointer', 'className' => 'relationClassName', 'objectId' => 'relationObjId'));
For example, if you want to relate a Book with a User via the Book's authors key, ObjectClassName would be Book, relationFieldName would be authors and relationClassName would be _User (remember Parse's special classes are prefixed with an underscore).
Let me know if this works for you too! It's a bit frustrating that I couldn't find anything about this on Parse's PHP documentation on relations. I had the same issue in JS before but at least this question helped me right away.

I had the same issue. There is a function in the SDK specifically for this that is more concise.
In \Parse\ParseOject:
$pointer = ParseObject::create('className', $id, true);
I found it in ParseObject.php :
/**
* Static method which returns a new Parse Object for a given class
* Optionally creates a pointer object if the objectId is provided.
*
* #param string $className Class Name for data on Parse.
* #param string $objectId Unique identifier for existing object.
* #param bool $isPointer If the object is a pointer.
*
* #return ParseObject
*/
public static function create($className, $objectId = null, $isPointer = false)

It sounds like you are trying to create a 1-many relationship. You can do this using ParseObject::getRelation, which will return an existing relation, or, create a new one (you can check out the definition here).
$obj = new ParseObject("ObjectClassName");
// get a relation (new or existing)
$relation = $obj->getRelation('myObjects');
// add objects to this relation
$relation->add([$myObject1, $myObject2, $myObject3]);
// save my object & my relation, with the new objects in it
$obj->save();
If you're trying to do many-many relations you'll want to create a Join table (technically a join 'class', like MyParseJoinClass) instead. If I misunderstood, and you're trying to perform a 1-1, you can always set an object as a property to automatically create a pointer
$obj->set('myPointer', $myPointedObject);
Internally you can use $obj->_toPointer to get an array composing a pointer referencing an object, but I would recommend you utilize the supported relation & pointer types in ParseObject instead, much easier to manage.

Related

getBeans() for a relationship returns empty array

I am creating a custom ACL class that would check whether the relationship exists between the records and if so load all the related records to that particular bean. I have looked at the sugar documentation which says to use load_relationship($relationshipName) for checking if relationship exists and getBeans() to load all the related records (as an array of objects). I have implemented this into my class but for some reason whichever module and relationship I use it always returns an empty array.
The data I use for checking has 3 parts:
The Module accessing the data
The relationship name with the target module (not module name)
The ID of the record accessing the data
The link here at sugar community shows a similar problem that I'm having, but the answer to this does not so solve my problem
Here is my custom ACL:
namespace Sugarcrm\Sugarcrm\custom\clients\base;
class CustomACL
{
const ACL_NONE = 0;
const ACL_READ_ONLY = 1;
const ACL_READ_WRITE = 2;
public static function checkRelated($module, $linkedRelationshipName, $id)
{
$bean = \BeanFactory::getBean($module);
if ($bean->load_relationship($linkedRelationshipName)) {
return self::checkRecordRelated($bean, $id,$linkedRelationshipName);
} else {
return false;
}
}
/**
* Checks if record is related
* #param $bean
* #param $id
* #param $linkedModule
* #return bool
*/
protected static function checkRecordRelated($bean, $id, $linkedModule)
{
$bean->retrieve_by_string_fields(array(
"id" => $id
));
if ($bean->load_relationship($linkedModule)) {
$relatedRecords = $bean->$linkedModule->getBeans();
return $relatedRecords;
} else {
return false;
}
}
}
This class should be working for any module, even if it is custom or non custom. I have tried using my custom module and even the default modules (leads, accounts etc) but none of them returns anything except an empty array.
I suspect the problem is that you are reusing the previously empty bean, for which you already loaded the same link using load_relationship() before.On the second load_relationship() call, Sugar probably returns the cached result from the first call (as the link is already internally flagged as having been loaded), therefore returning the same empty array again.
Therefore instead of using
$bean->retrieve_by_string_fields(array(
"id" => $id
));
I'd suggest creating a new bean e.g. using
if (empty($id)) {
return false;
}
$bean = BeanFactory::retrieveBean($module, $id);
if (!$bean) {
return false;
}
(which should actually not be too slow, as the bean is probably cached already)
Notes:
Your variable names are somewhat confusing. $linkedRelationshipName and $linkedModule should contain neither the relationship name nor the module name, but the name of the link-type field.
EDIT:
To reiterate:
The documentation may be misleading there, but load_relationship() does not expect the relationship name as parameter. What it expects is the link name!.
from data/SugarBean.php:
/**
* Loads the request relationship. This method should be called before performing any operations on the related data.
*
* This method searches the vardef array for the requested attribute's definition. If the attribute is of the type
* link then it creates a similary named variable and loads the relationship definition.
*
* #param string $link_name link/attribute name.
*
*#return nothing.
*/
function load_relationship($link_name)
So make sure to check the VarDefs of each module for the correct link name.
E.g.
relationship name: accounts_contacts
link field for this relationship in account: contacts,so you should be calling $accountBean->load_relationship('contacts')
link field for this relationship in contact: accounts,so you should be calling $contactBean->load_relationship('accounts')
Note: link names are basically arbitrary across different modules, don't rely on them being lowercase singular/plural of the linked module. In some cases (and for custom relationships) they will not be.

Symfony4 - How to update a doctrine ArrayCollection ?

I have a member of my entity is an arrayCollection. With a classic form builder is working fine, I can select multiple items and persist it. But when I try to update an object in controller I get the error : "Call to a member function setFaavailability() on array".
A resume of my entity :
/**
* #ORM\ManyToOne(targetEntity="App\Entity\FaAvailability",
inversedBy="faavailability")
* #ORM\JoinColumn(nullable=true)
* #ORM\Column(type="array")
*/
public $faavailability;
/**
* #return mixed
*/
public function getFaavailability()
{
return $this->faavailability;
}
/**
* #param mixed $faavailability
*/
public function setFaavailability($faavailability)
{
$this->faavailability = $faavailability;
}
In my controler :
$varFaavailability = $animal->faperson->getFaavailability();
foreach($varFaavailability as $availability){
if($availability->getName() == $animal->typepet->getName()){
$varFaavailability->removeElement($availability);
$faPerson = $em->getRepository(FaPerson::class) >findById($animal->faperson->getId());
$faPerson->setFaavailability($varFaavailability);
$em->persist($faPerson);
$em->flush();
}
}
Any ideas ?
If I remember well, when you set a field as an ArrayCollection it means that you have a oneToMany relationship between two entities.
From your code, I can tell you that you are trying to persist the data in the wrong entity. You usually add the owning_entity_id(1-to-N) in each item(1-to-N) and persist it. In your code, you are trying to set all the references at once, which is never going to happen. Delete the setFaavailability() or redefine the entities' relationships.
You should never try to mass-add foreign key relationships in one super duper setter function. Cycle through all the items and set the reference to the "parent" entity.
The problem is in this part: $faPerson = $em->getRepository(FaPerson::class)->findById($animal->faperson->getId());
The findBy* methods will try to find multiple entities and return them in a Collection.
If you're looking for a single person, you can use findOneById instead. Or (assuming id is configured as identifier in Doctrine) you can even use the find method: $faPerson = $em->getRepository(FaPerson::class)->find($animal->faperson->getId());
some general comments:
In Doctrine you never have to work with the IDs. Use the entity
objects! You only need to findById if you get the ID from a request parameter for example.
You should reconsider the naming of your variables to make it clear if it is a collection ($availabilities) or a single one ($availability).
Always use the getter/setter methods instead of the fields (typepet vs getTypepet()).
Call flush() one at the end to update all entities in one single transaction.
I've renamned the variables below as I understood them. However I am still not sure what $animal->faperson->getFaavailabilities() returns, since at the beginning you wanto to loop through the results and later set it to a single one via setFaavailability()?
//Should be a Doctrine ArrayCollection
$varFaavailabilities = $animal->faperson->getFaavailabilities();
foreach($varFaavailability as $availability){
if($availability->getName() == $animal->getTypepet()->getName()) {
//Why do you want to remove an element from the current loop?
$varFaavailability->removeElement($availability);
//No need to use Id
$faPerson = $animal->getFaperson();
//A single one?
$faPerson->setFaavailability($availability);
//More than one? addFaavailability should exist.
$faPerson->addFaavailability($availability);
$em->persist($faPerson);
}
}
$em->flush();

Access keys in json type column for Eloquent collections?

A table column in my database is saved as json type. I want to display a keyed value in the saved json object on my blade templates but I'm unsure how.
Let's say I have $table->json('meta') in my table schema for Newsletter model, with meta column having e.g. {"foo": "bar"} as value.
How would I retrieve something like $newsletter->meta->foo? Since $newsletter->meta returns string instead of json object by default on Laravel 5.5 requiring a trivial json_decode to convert it.
A cleaner solution besides json_decode on every call, would be to use an accessor on that column e.g. getMetaAttribute but that's still messy. I want automatic json column to PHP object detection, how can I make this happen?
You can declare a protected $casts array inside your model in which you can instruct Eloquent to automatically convert types of model attributes. In your case that would look like this:
/*
* #property string $meta - json is actually just a string
*/
class Newsletter extends Model {
protected $casts = [
'meta' => 'array',
];
}
// Now you can use `$meta` as array:
$newsletter = Newsletter::find(1);
$foo = array_get($newsletter->meta, 'foo');
But this still does not convert it into an object. Although object is mentioned in the docs as being a valid cast type, I can not tell you exactly what it does.
As a note, these $casts are not bi-directional. If you want to set meta on a Newsletter instance, you'd have to create your own facilities to convert any object or array into a json string. Eloquent allows you to define mutators on your model to get the job done.

Doctrine 2 - How to use objects retrieved from cache in relationships

I'm working in a project that use Doctrine 2 in Symfony 2 and I use MEMCACHE to store doctrine's results.
I have a problem with objects that are retrieved from MEMCACHE.
I found this post similar, but this approach not resolves my problem: Doctrine detaching, caching, and merging
This is the scenario
/**
* This is in entity ContestRegistry
* #var contest
*
* #ORM\ManyToOne(targetEntity="Contest", inversedBy="usersRegistered")
* #ORM\JoinColumn(name="contest_id", referencedColumnName="id", onDelete="CASCADE"))
*
*/
protected $contest;
and in other entity
/**
* #var usersRegistered
*
* #ORM\OneToMany(targetEntity="ContestRegistry", mappedBy="contest")
*
*/
protected $usersRegistered;
Now imagine that Contest is in cache and I want to save a ContestRegistry entry.
So I retrieve the object contest in cache as follows:
$contest = $cacheDriver->fetch($key);
$contest = $this->getEntityManager()->merge($contest);
return $contest;
And as last operation I do:
$contestRegistry = new ContestRegistry();
$contestRegistry->setContest($contest);
$this->entityManager->persist($contestRegistry);
$this->entityManager->flush();
My problem is that doctrine saves the new entity correctly, but also it makes an update on the entity Contest and it updates the column updated. The real problem is that it makes an update query for every entry, I just want to add a reference to the entity.
How I can make it possible?
Any help would be appreciated.
Why
When an entity is merged back into the EntityManager, it will be marked as dirty. This means that when a flush is performed, the entity will be updated in the database. This seems reasonable to me, because when you make an entity managed, you actually want the EntityManager to manage it ;)
In your case you only need the entity for an association with another entity, so you don't really need it to be managed. I therefor suggest a different approach.
Use a reference
So don't merge $contest back into the EntityManager, but grab a reference to it:
$contest = $cacheDriver->fetch($key);
$contestRef = $em->getReference('Contest', $contest->getId());
$contestRegistry = new ContestRegistry();
$contestRegistry->setContest($contestRef);
$em->persist($contestRegistry);
$em->flush();
That reference will be a Proxy (unless it's already managed), and won't be loaded from the db at all (not even when flushing the EntityManager).
Result Cache
In stead of using you own caching mechanisms, you could use Doctrine's result cache. It caches the query results in order to prevent a trip to the database, but (if I'm not mistaken) still hydrates those results. This prevents a lot of issues that you can get with caching entities themselves.
What you want to achieve is called partial update.
You should use something like this instead
/**
* Partially updates an entity
*
* #param Object $entity The entity to update
* #param Request $request
*/
protected function partialUpdate($entity, $request)
{
$parameters = $request->request->all();
$accessor = PropertyAccess::createPropertyAccessor();
foreach ($parameters as $key => $parameter) {
$accessor->setValue($entity, $key, $parameter);
}
}
Merge requires the whole entity to be 100% fullfilled with data.
I haven't checked the behavior with children (many to one, one to one, and so on) relations yet.
Partial update is usually used on PATCH (or PUT) on a Rest API.

Symfony/Doctrine

I am currently learning Symfony and Doctrine by reading the docs.
I don't understand the difference between find and findOneById. I tried to use them both in this simple example and it looks they do the same thing to me.
$product = $this->getDoctrine()
->getRepository('AcmeStoreBundle:ProductEntity')
->findOneById($id);
Are they really the same thing or there is some difference? And where I can find the detailed documentation for all these methods?
In your case, they happen to do the same thing. Looking at this example, you'll notice that find() looks for the field named after the primary key. findOneBy<Field>() will explicitly use the field in the name of the method, even if it's not the primary key, and will return the first record. So, in the end, if the primary key is indeed named id, then both will do the same thing.
// query by the primary key (usually "id")
$product = $repository->find($id);
// dynamic method names to find based on a column value
$product = $repository->findOneById($id);
$product = $repository->findOneByName('foo');
There is an API here I don't think there is any difference: the two methods, when call the way you call them, do this:
return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($id);
But find will be quicker and far quicker in some cases, because it doesn't use the __call magic method, and because find() checks a map of the current unit of work before whereas load() doesn't (see the #todo):
/**
* Loads an entity by a list of field criteria.
* ...
*
* #todo Check identity map? loadById method? Try to guess whether $criteria is the id?
*/
public function load(array $criteria, $entity = null, $assoc = null, array $hints = array(), $lockMode = 0)
So prefer find(), findOneById() is just a less efficient method to do the same thing.
In fact, is not the same thing.
Think about it. If you call "findBy()" you assume you'll receive a collection of entities ( 0, 1 or more than one ). So, to get all results, you'll need to iterate ArrayCollection or just get first ( $result->first() ).
If your query is by a unique key ( As this case ), you can just get unique entity by calling "getOneById()" and you will receive the entity as result.
/**
* Retrieving Product with 'findOneBy'
*/
$product = $this->getDoctrine()
->getRepository('AcmeStoreBundle:ProductEntity')
->findOneById($id);
/**
* Retrieving Product with 'findBy'
*/
$product = $this->getDoctrine()
->getRepository('AcmeStoreBundle:ProductEntity')
->findById($id)
->first();
Semantically, the first one it the best.
*TIP
Entity should be called just Product.
Why? Because is under "/Entity" folder ( Almost, should... ), and namespace will contain info about "What is exactly Product"
// query by the primary key (usually "id")
$product = $repository->find($id);
// dynamic method names to find based on a column value
$product = $repository->findOneById($id);
// $foo is any name which you want to find from database
$product = $repository->findOneByName($foo);
It calls the same method in the end.
findByKey('value')
Is basically the same as
findBy(array('key' => 'value'))
Where key is the property of the entity and value is the value of the property.
findById($id)
Is a special case of the above. And so is
find($id)
All of these methods execute the same query in the end. However, there is a difference in
findBy()
and
findOneBy()
Where findOneBy() only returns a single result and findBy will return all the results satisfying the demands.
However, in general it is considered good practice to use DQL queries instead. Consider lazy loading, array hydration, prepared statements, etc.
This is an interesting article on the topic:
Some Doctrine 2 Best Practices
Is the same thing, but I prefer the findOneBy method. It's more clear.

Categories