Access parent object in method - php

I have a problem i can't even name properly. Here is the situation.
I'm using php framework (kohana3 but it's not important i think) to write a simple cms with entries and uploads. The relations are:
Entries -> has_many -> Uploads
Uploads -> belongs_to -> Entries
To display all entries with their uploads, i'm using this code in view file:
foreach( $entries as $entry )
{
foreach( $entry->upload->find_all() as $uploads )
{
foreach( $uploads->find_all() as $upload )
{
echo $upload->file;
}
}
}
Now i want to create a method in Entry model called find_first_upload() which will return first uploaded element. Here is the future usage of it:
foreach( $entries as $entry )
{
echo $entry->find_first_upload()->file;
}
and the important thing is that i don't want to pass any variables to find_first_upload() method like for example $entry object or currently looped entry id.
What i want to achieve is to be able to get currently looped entry object inside find_first_upload method - it'll allow me to make a foreach's inside of it.
Have you any ideas how can i code that?
If you have any questions, feel free to ask them here.
Thanks,
Mike

public function find_first_upload() {
$result = 0;
foreach( $this->upload->find_all() as $uploads )
{
foreach( $uploads->find_all() as $upload )
{
if(empty($result))
$result = $upload;
}
}
return $result;
}

Sorry for reviving this old topic, but I was googling something and stumbled on this. In case anyone has a similar issue, ignore the accepted answer, this is the correct way:
public function find_first_upload()
{
return $this->uploads->find();
}

Related

Fetch data from many to many relationship with eloquent

I have 2 models, store and dvd with many to many relationship
dvd model:
public function stores() {
return $this->belongsToMany('App\store');
}
store model:
public function dvds ( ) {
return $this->belongsToMany('App\dvd');
}
Then, in controller, when I need fetch data from both models, I use code like this:
$res_store = store::orderBy("id","desc")->get();
foreach( $res_store as $row ) {
$dvds = $row->dvds()->get();
foreach ($dvds as $dvd) {
// echo columns here
}
}
This works, but my question is, I'm doing this in correct way? I'm asking because it seems 2 loops for this simple relation is somehow inefficient.
No, this is not the right way.
When looping over a $store, you can access the associated $dvd records via $store->dvds; there is no need to call $row->dvds()->get(), as that is executing a new query with the same result of $store->dvds. Full code should simply be:
$stores = Store::with("dvds")->orderBy("id", "DESC")->get();
foreach($stores AS $store){
foreach($store->dvds AS $dvd){
... // Do something with `$dvd`
}
}
The ::with("dvds") clause is known as "Eager loading", and prevents $store->dvds from needing execute another query behind the scenes.
Also, please name your models correctly. Classes in PHP are StudlyCase, so Store and DVD, not store and dvd.

PHP/Propel delete record 1:n

I've got two tables: step and links joined 1:n. I'm aiming to maintain the links through the step objects. I retrieve all steps from the database and populate the relation with the links table. I persist the step object containing a collection of links to JSON and return it to the front end using REST.
That means that if a step is linked or unlinked to another step in the front end I send the entire step back to the backend including a collection of links. In the back end I use the following code:
public function put($processStep) {
if (isset($processStep['Processesid']) && isset($processStep['Coordx']) && isset($processStep['Coordy'])) {
$p = $this->query->findPK($processStep['Id']);
$p->setId($processStep['Id']);
$p->setProcessesid($processStep['Processesid']);
if (isset($processStep['Flowid'])) $p->setFlowid($processStep['Flowid']);
if (isset($processStep['Applicationid'])) $p->setApplicationid($processStep['Applicationid']);
$p->setCoordx($processStep['Coordx']);
$p->setCoordy($processStep['Coordy']);
$links = $p->getLinksRelatedByFromstep();
$links->clear();
foreach ($processStep['Links'] as $link) {
if (!isset($link['Linkid'])) {
$newLink = new \Link();
$newLink->setFromstep($link['Fromstep']);
$newLink->setTostep($link['Tostep']);
$links->prepend($newLink);
}
}
$p->save();
return $p;
} else {
throw new Exceptions\ProcessStepException("Missing mandatory fields.", 1);
}
}
I'm basically deleting every link from a step and based upon the request object I recreate the links. This saves me the effort to compare what links are deleted and added. The insert work like a charm Propel automatically creates the new links. Thing is it doesn't delete like it inserts. I've checked the object that is being persisted ($p) and I see the link being deleted but in the MySQL log there is absolutely no action being performed by Propel. It looks like a missing member from the link collection doesn't trigger a dirty flag or something like that.
Maybe I'm going about this the wrong way, I hope someone can offer some advice.
Thanks
To delete records, you absolutely always have to use delete. The diff method on the collection is extremely helpful when determining which entities need added, updated, and deleted.
Thanks to Ben I got on the right track, an explicit call for a delete is not needed. I came across a function called: setRelatedBy(ObjectCollection o) I use this function to provide a list of related objects, new objects are interpreted as inserts and omissions are interpreted as deletes.
I didn't find any relevant documentation regarding the problem so here's my code:
$p = $this->query->findPK($processStep['Id']);
$p->setId($processStep['Id']);
$p->setProcessesid($processStep['Processesid']);
$p->setCoordx($processStep['Coordx']);
$p->setCoordy($processStep['Coordy']);
if (isset($processStep['Flowid'])) $p->setFlowid($processStep['Flowid']);
if (isset($processStep['Applicationid'])) $p->setApplicationid($processStep['Applicationid']);
//Get related records, same as populaterelation
$currentLinks = $p->getLinksRelatedByFromstep();
$links = new \Propel\Runtime\Collection\ObjectCollection();
//Check for still existing links add to new collection if so.
//This is because creating a new Link instance and setting columns marks the object as dirty creating an exception due to duplicate keys
foreach ($currentLinks as $currentLink) {
foreach ($processStep['Links'] as $link) {
if (isset($link['Linkid']) && $currentLink->getLinkid() == $link['Linkid']) {
$links->prepend($currentLink);
break;
}
}
}
//Add new link objects
foreach ($processStep['Links'] as $link) {
if (!isset($link['Linkid'])) {
$newLink = new \Link();
$newLink->setFromstep($link['Fromstep']);
$newLink->setTostep($link['Tostep']);
$links->prepend($newLink);
}
}
//Replace the collection and save the processstep.
$p->setLinksRelatedByFromstep($links);
$p->save();

Kohana 3.2 ORM check if a record exists in a collection

I'm trying to print a total list with records, for this list, some might be connected to a user. The active items are from another collection (the active collection) of the same class. I'll foreach over the total list and need to check for every record if it extists in the active collection.
Is there a function for that?
At the moment I put the active items in an array with the record id as array key to check on, and that works, but I wondered if there is a nicer way to do this?
$totalcollection = ORM::Factory('table')->find_all();
// user has an relation named 'activerecords', which will find the records connected
// to a user through a link table
$activecollection = $user->activerecords->find_all();
foreach( $totalcollection as $record ) {
$active = false;
// I'm looking for a function like this one. Does it exist?
$active = $activecollection->contains($record);
if($active) {
echo "<li class=\"active\">".$record->name."</li>";
} else {
echo "<li>".$record->name."</li>";
}
}
Any ideas?
You could write a method for that in your Model_Table
public function belongs_to_user(Model_User $user)
{
return ($user->activerecords->where('id', '=', $this->id)->count_all() > 0);
}
And then in your loop
foreach( $totalcollection as $record ) {
if($record->belongs_to_user($user)) {
echo "<li class=\"active\">".$record->name."</li>";
} else {
echo "<li>".$record->name."</li>";
}
}
Another option, if you think this will generate too many SQL queries, would be to get the activerecord ids and check with in_array(), I think thats the way you're doing it at the moment. Would be fine, too, IMO.
BTW: Please consider separating your layout from your logic, e.g. with KOstache.
Since you're using a single table you just need to check whether the type property of an element from $totalcollection is equal active, like this:
$totalcollection = ORM::Factory('table')->find_all();
foreach( $totalcollection as $record ) {
if($record->type == 'active') {
echo "<li class=\"active\">".$record->name."</li>";
} else {
echo "<li>".$record->name."</li>";
}
}
Treating them as two separate collections makes no sense and is an unnecessary overhead.

Yii delete with multiple active records

I'm after some code advice. I've got two models which are dependent on each other. When one of the models gets deleted I want to make sure both records in the database are deleted.
I handle this in one direction using foreign keys so if the parent gets deleted to. But as these rows are both dependent on each other I need the same functionality to happen in the child.
In the child model I've overloaded the delete method so it looks like this:
public function delete() {
$cameraTransaction = $this->dbConnection->beginTransaction();
try
{
$this->ftpuser->delete();
if($this->beforeDelete())
{
$result=$this->deleteByPk($this->getPrimaryKey())>0;
$this->afterDelete();
}
$cameraTransaction->commit();
}
catch(Exception $e) // an exception is raised if a query fails
{
$cameraTransaction->rollBack();
}
}
I've tested and this seems to work well. I wondered if an expert / guru could confirm whether I've done the right thing :)
Thanks
Alan
I think you are doing it right. You can make it something more general.
Every model has relations defined, make some(or all) the relations deletable. You have to flag them ( in someway) deletable along with current record.
I would define a new function besides relations() in the model.
function deletableRelations() {
return array('ftpuser', 'childs', .....);
}
and define some generic function (please include DB transactions in this function.)
function DeleteRelationships($model) {
foreach( $model->deletableRelations() as $relation ) {
if( $model->$relation == null) continue;
if( is_array($model->$relation)) {
foreach( $model->$relation as $relRecord ) {
$relRecord->delete();
}
}
else {
$model->$relation->delete();
}
}
}
And this generic function can be used any of your models.
Make sure that recursion does not happen here (i.e. parent deletes child ... but child also tries to delete parent. )

Symfony2: ACL - Using findAcls()

I am having a problem with attempting to batch load ACLs for a specific object (in the example below it is the Account class.)
If I am using the following code, even though acl_object_identities is populated, the returning array that should be populated is empty. What am I missing?
$oids = array();
foreach ($accounts as $account) {
$oid = ObjectIdentity::fromDomainObject($account);
$oids[] = $oid;
}
$aclProvider->findAcls($oids);
$accounts holds the array of entities found with a findAll().
Well, it looks like the ACLs are pulled after all with the key being an iterative follow-up to see what permissions are in place.
foreach ($accounts as $account) {
if ($securityContext->isGranted('EDIT', $account)) {
// Granted, do something with it
} else {
// Not Granted
}
}
So, it seems that everything is working a designed.

Categories