I am building an API with Lumen, and this particular part of my application contains the following relationships:
Promotions hasMany Items
Items hasMany images
On an image upload, a row is inserted which provides the URL to the uploaded image. The image is uploaded to public/assets/promotion_$id/item_$id.
When an item is deleted, so too does its respective image folder. That part was relatively easy, and can be done from inside my PromotionController:
public function delete_item(Request $request)
{
$item = PromotionItems::find($request->input('id'))->toArray();
$img_folder_path = 'assets/promotions/promotion_' . $item['promotion_id'] . '/item_' . $item['id'];
if(is_dir($img_folder_path))
{
$files = array_diff(scandir($img_folder_path), array('.','..'));
foreach ($files as $file)
{
unlink($img_folder_path . '/' . $file);
}
rmdir($img_folder_path);
}
$delete = PromotionItems::destroy($request->input('id'));
return response()->json($delete);
}
This is okay, but this also needs to happen when a promotion is deleted. Right now, when a promotion is deleted, an event is called that also deletes items. When an item is deleted, an event is called that also deletes the image rows in the database. So it's a nice, neat chain of events.
But, it will only delete the image folder if an item is being deleted directly, which is why I would like to put this code in my delete event in my item model:
protected static function boot()
{
parent::boot();
static::deleting(function($item)
{
$item->images()->delete();
});
}
However, this function's scope doesn't appear to extend to the actual object that's being deleted. And since the image path contains IDs, I need access to the item's properties before it's deleted.
I'm relatively new to Laravel/Lumen/Eloquent, so any advice would be appreciated.
Edit: I'm aware that I could technically put the folder delete code inside my Promotion controller in its delete function, and just call all the items and loop through them, deleting all their folders/files. But that's redundant code and I'd like to avoid it.
It occurred to me that I'd have to do a folder delete on promotion delete anyway, because I'll want to delete its top level directory. So I just did a sort of recursive function in my controller:
public function remove_directory($path)
{
if(is_dir($path))
{
$files = array_diff(scandir($path), array('.','..'));
foreach ($files as $file)
{
(is_dir("$path/$file")) ? $this->remove_directory("$path/$file") : unlink($path . '/' . $file);
}
rmdir($path);
}
}
So when I call it from my delete_promotion function, it deletes the promotion folder and all folders/files inside of it. And I just call the same function but with the /item_$id on the path in my delete_item function.
Related
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();
I want to delete all record according to id and then insert record in same table,I tried many ways but can't find solution please help me.
Basically as per the document id i want to delete all document but it is not working.
Here is my controller code:
foreach ($receievers as $user) {
$this->shareRepo->deleteSharedDoc($resourceId);
$this->shareRepo->saveshareSharedDoc($resourceId, $user->id,$this->getCurrentUser());
}
The repository code:
function saveSharedDoc($resourceId, $sharedWith, $resourceOwnerId){
$shareDocs = new ShareDocs;
$shareDocs->resource_id = $resourceId;
$shareDocs->shared_with = $sharedWith;
$shareDocs->user_id = $resourceOwnerId;
$shareDocs->shared_on = $this->getCurrentDateTime();
$shareDocs->token = str_random(20);
$shareDocs->save();
return $shareDocs->token;
}
function deleteSharedDoc($resourceId){
$network = ShareDocs::where('resource_id','=',$resourceId);
$result=$network->delete();
return $result;
}
Please help me out
It's seems you're doing it correctly. But there are two things that you have to change.
You are calling to saveshareSharedDoc method within foreach loop to save data. but actual method name on your repo is saveSharedDoc. (there two "share" words on loop)
you can return deleted rows directly return ShareDocs::where('resource_id', $resourceId)->delete();
I am inserting an xml file contents into a DB from my localstorage. However what happens is that the xml file can be updated and when its updated i need to check if the record already exists or not in my DB. If it exists i need to update the record and if it doesn't exist i need to insert a new record. I am able to do the insert but I dont seem to find a Laravel way to do this using update method.
In my xml file they is a node that doesn't change value when the xml has been updated. I am thinking I must use an if statement to check against this node's value but I cant seem to get my head around this.
Below is how I am doing my insert.
public function store(Request $request)
{
// Building directory path.
$directory = storage_path('app/xmlentries/uploads');
$files = File::allFiles($directory);
foreach($files as $file) {
$contents = $file->getContents();
foreach((array) $contents as $content)
{
$simpleXml = simplexml_load_string($xml);
$data = [
'requisition' => $simpleXml->ExportData->Requisition['clientReqId'], //NODE THAT DOES NOT CHANGE VALUE
'experience' => $simpleXml->ExportData->Requisition->EssentialFunction,
'job_requirements' => $simpleXml->ExportData->Requisition->EssentialFunction,
],
];
Vacancy::insert($data); //WHERE I AM INSERTING MY RECORD
}
}
}
How would I update the record if it already exists?
https://laravel.com/docs/5.2/eloquent#basic-inserts
If you use Eloquent you don't need a "if" in your controller
the "save" method will see it for you, if your PKs was correctly configured in your model
The "Vacancy" is your model right? put every attribute in an object
$vacancy = new Vencancy();
$vacancy->requisition = $simpleXml->ExportData->Requisition['clientReqId'];
$vacancy->save();
try it with all your attributes....
see your primary key [link]https://laravel.com/docs/5.2/eloquent#defining-models[/link]
How do you change the order of files in the file system? I am using Fetch.
The reason I ask is because I have a menu bar that automatically lists the pages in my website by what is in the "pages" folder. The way I see it on fetch, they are in alphabetical order "blog.ctp, games.ctp, home.ctp, news.ctp". So I would expect the menu bar to list the pages as "BLOG GAMES HOME NEWS", but instead it lists them as "GAMES NEWS BLOG HOME". Ultimately I want the order to be "HOME GAMES BLOG NEWS". How do I change the order of the files?
Here is my code in case it is helpful. But my code is not the problem...I just need to know how to change the file order in the folder "Pages".
if ($handle=opendir('../View/Pages/'))
{
while (false !== ($entry = readdir($handle)))
{
if(strpos($entry,".ctp")!==False)
echo "<div class='menubarItem'>".$this->Html->link(substr($entry,0,-4),
array('controller'=>'pages','action'=>'display',substr($entry,0,-4)),
array('escape' => false)).'</div>';
}
}
Add a priority flag to your file names at the beginning or end. I.e. GAMES_1, HOME_2 ...etc... Sort the array of file names using PHP sort() and replace the last two character from file name using substr($filename, -2).
The CakePHP Folder utility has the option to sort the result;
http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::find
App::uses('Folder', 'Utility');
App::uses('Folder', 'Utility');
$myFolder = new Folder('/path/to/directory');
$files = $myFolder->find('*.ctp', true);
foreach ($files as $filename) {
// your code here
}
However, If you do not want to show the pages alphabetically, either prefix the file names with a number and create a special route or don't use the filename for sorting but manually specify the order.
In the end, the only reason for dynamically creating the menu based on the View files is to automatically have the menu generated for you. However, chances are that these changes won't occur often and based on your comment (prefered order) you do have a specific order in mind.
The best solution is to just manually specify the sort order. This will also improve performance as the server will not have to do a directory-scan for each request
For example:
/**
* MenuItems
*
* preferable pass this as a viewVar to the View
*
* file => title
*/
$menuItems = array(
'home' => 'HOME',
'blog' => 'BLOG',
....
);
foreach($menuItems as $file => $title) {
// your code here
}
moving this to a Model
Retrieving the file list inside your View is not the location where this should be done. It's best to read the files beforehand and pass them to the View via a viewvar. Because reading the files is actually 'retrieving data', you may want to create a Model for this that is not connected to a database table.
example
app/Model/Menuoption.php
App::uses('Folder', 'Utility');
class Menuoption extends AppModel {
// this model does not use a database table
public $useTable = false;
public function getOptions()
{
// NOTE: or use the 'manually' created menuItems here
$myFolder = new Folder('/path/to/directory');
return $myFolder->find('*.ctp', true);
}
}
Inside your controller; for example in the beforeRender() callback, or inside a regular action;
public function beforeRender()
{
$this->set('files', ClassRegistry::init('Menuoption')->getOptions());
}
inside your view, you can now access the results via the $files variable;
foreach ($files as $filename) {
// your code here
}
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();
}