Manage files and directories in browser - php
I'm using a PHP based CMS called Couch (CouchCMS).
The interface is pretty simple. It's easy to edit page content and that's basically it.
I'm now trying to add functionality. I want to add a tab called "files". This tab when activated is going to display all my files. For example it will show me my "images" folder and all the other files located in my website's folder.
Is it possible to somehow with PHP show all my directories on my web page and from there add and remove them?
Try using this function: http://php.net/manual/en/function.readdir.php
But I think you can use jQuery + PHP libraries that do this for you:
https://www.sitepoint.com/10-jquery-file-manager-plugins/
This is a php code used for the Silverstripe cms, don't know if it might work on your side.
<?php
/**
* AssetAdmin is the 'file store' section of the CMS.
* It provides an interface for manipulating the File and Folder objects in the system.
*
* #package cms
* #subpackage assets
*/
class AssetAdmin extends LeftAndMain implements PermissionProvider{
private static $url_segment = 'assets';
private static $url_rule = '/$Action/$ID';
private static $menu_title = 'Files';
private static $tree_class = 'Folder';
/**
* Amount of results showing on a single page.
*
* #config
* #var int
*/
private static $page_length = 15;
/**
* #config
* #see Upload->allowedMaxFileSize
* #var int
*/
private static $allowed_max_file_size;
private static $allowed_actions = array(
'addfolder',
'delete',
'AddForm',
'DeleteItemsForm',
'SearchForm',
'getsubtree',
'movemarked',
'removefile',
'savefile',
'deleteUnusedThumbnails' => 'ADMIN',
'doSync',
'filter',
);
/**
* Return fake-ID "root" if no ID is found (needed to upload files into the root-folder)
*/
public function currentPageID() {
if(is_numeric($this->request->requestVar('ID'))) {
return $this->request->requestVar('ID');
} elseif (is_numeric($this->urlParams['ID'])) {
return $this->urlParams['ID'];
} elseif(Session::get("{$this->class}.currentPage")) {
return Session::get("{$this->class}.currentPage");
} else {
return 0;
}
}
/**
* Set up the controller, in particular, re-sync the File database with the assets folder./
*/
public function init() {
parent::init();
// Create base folder if it doesnt exist already
if(!file_exists(ASSETS_PATH)) Filesystem::makeFolder(ASSETS_PATH);
Requirements::javascript(CMS_DIR . "/javascript/AssetAdmin.js");
Requirements::javascript(CMS_DIR . '/javascript/CMSMain.GridField.js');
Requirements::add_i18n_javascript(CMS_DIR . '/javascript/lang', false, true);
Requirements::css(CMS_DIR . "/css/screen.css");
$frameworkDir = FRAMEWORK_DIR;
Requirements::customScript(<<<JS
_TREE_ICONS = {};
_TREE_ICONS['Folder'] = {
fileIcon: '$frameworkDir/javascript/tree/images/page-closedfolder.gif',
openFolderIcon: '$frameworkDir/javascript/tree/images/page-openfolder.gif',
closedFolderIcon: '$frameworkDir/javascript/tree/images/page-closedfolder.gif'
};
JS
);
CMSBatchActionHandler::register('delete', 'AssetAdmin_DeleteBatchAction', 'Folder');
}
/**
* Returns the files and subfolders contained in the currently selected folder,
* defaulting to the root node. Doubles as search results, if any search parameters
* are set through {#link SearchForm()}.
*
* #return SS_List
*/
public function getList() {
$folder = $this->currentPage();
$context = $this->getSearchContext();
// Overwrite name filter to search both Name and Title attributes
$context->removeFilterByName('Name');
$params = $this->request->requestVar('q');
$list = $context->getResults($params);
// Don't filter list when a detail view is requested,
// to avoid edge cases where the filtered list wouldn't contain the requested
// record due to faulty session state (current folder not always encoded in URL, see #7408).
if(!$folder->ID
&& $this->request->requestVar('ID') === null
&& ($this->request->param('ID') == 'field')
) {
return $list;
}
// Re-add previously removed "Name" filter as combined filter
// TODO Replace with composite SearchFilter once that API exists
if(!empty($params['Name'])) {
$list = $list->filterAny(array(
'Name:PartialMatch' => $params['Name'],
'Title:PartialMatch' => $params['Name']
));
}
// Always show folders at the top
$list = $list->sort('(CASE WHEN "File"."ClassName" = \'Folder\' THEN 0 ELSE 1 END), "Name"');
// If a search is conducted, check for the "current folder" limitation.
// Otherwise limit by the current folder as denoted by the URL.
if(empty($params) || !empty($params['CurrentFolderOnly'])) {
$list = $list->filter('ParentID', $folder->ID);
}
// Category filter
if(!empty($params['AppCategory'])
&& !empty(File::config()->app_categories[$params['AppCategory']])
) {
$exts = File::config()->app_categories[$params['AppCategory']];
$list = $list->filter('Name:PartialMatch', $exts);
}
// Date filter
if(!empty($params['CreatedFrom'])) {
$fromDate = new DateField(null, null, $params['CreatedFrom']);
$list = $list->filter("Created:GreaterThanOrEqual", $fromDate->dataValue().' 00:00:00');
}
if(!empty($params['CreatedTo'])) {
$toDate = new DateField(null, null, $params['CreatedTo']);
$list = $list->filter("Created:LessThanOrEqual", $toDate->dataValue().' 23:59:59');
}
return $list;
}
public function getEditForm($id = null, $fields = null) {
$form = parent::getEditForm($id, $fields);
$folder = ($id && is_numeric($id)) ? DataObject::get_by_id('Folder', $id, false) : $this->currentPage();
$fields = $form->Fields();
$title = ($folder && $folder->exists()) ? $folder->Title : _t('AssetAdmin.FILES', 'Files');
$fields->push(new HiddenField('ID', false, $folder ? $folder->ID : null));
// File listing
$gridFieldConfig = GridFieldConfig::create()->addComponents(
new GridFieldToolbarHeader(),
new GridFieldSortableHeader(),
new GridFieldFilterHeader(),
new GridFieldDataColumns(),
new GridFieldPaginator(self::config()->page_length),
new GridFieldEditButton(),
new GridFieldDeleteAction(),
new GridFieldDetailForm(),
GridFieldLevelup::create($folder->ID)->setLinkSpec('admin/assets/show/%d')
);
$gridField = GridField::create('File', $title, $this->getList(), $gridFieldConfig);
$columns = $gridField->getConfig()->getComponentByType('GridFieldDataColumns');
$columns->setDisplayFields(array(
'StripThumbnail' => '',
// 'Parent.FileName' => 'Folder',
'Title' => _t('File.Name'),
'Created' => _t('AssetAdmin.CREATED', 'Date'),
'Size' => _t('AssetAdmin.SIZE', 'Size'),
));
$columns->setFieldCasting(array(
'Created' => 'Date->Nice'
));
$gridField->setAttribute(
'data-url-folder-template',
Controller::join_links($this->Link('show'), '%s')
);
if($folder->canCreate()) {
$uploadBtn = new LiteralField(
'UploadButton',
sprintf(
'<a class="ss-ui-button ss-ui-action-constructive cms-panel-link" data-pjax-target="Content" data-icon="drive-upload" href="%s">%s</a>',
Controller::join_links(singleton('CMSFileAddController')->Link(), '?ID=' . $folder->ID),
_t('Folder.UploadFilesButton', 'Upload')
)
);
} else {
$uploadBtn = null;
}
if(!$folder->hasMethod('canAddChildren') || ($folder->hasMethod('canAddChildren') && $folder->canAddChildren())) {
// TODO Will most likely be replaced by GridField logic
$addFolderBtn = new LiteralField(
'AddFolderButton',
sprintf(
'<a class="ss-ui-button ss-ui-action-constructive cms-add-folder-link" data-icon="add" data-url="%s" href="%s">%s</a>',
Controller::join_links($this->Link('AddForm'), '?' . http_build_query(array(
'action_doAdd' => 1,
'ParentID' => $folder->ID,
'SecurityID' => $form->getSecurityToken()->getValue()
))),
Controller::join_links($this->Link('addfolder'), '?ParentID=' . $folder->ID),
_t('Folder.AddFolderButton', 'Add folder')
)
);
} else {
$addFolderBtn = '';
}
if($folder->canEdit()) {
$syncButton = new LiteralField(
'SyncButton',
sprintf(
'<a class="ss-ui-button ss-ui-action ui-button-text-icon-primary ss-ui-button-ajax" data-icon="arrow-circle-double" title="%s" href="%s">%s</a>',
_t('AssetAdmin.FILESYSTEMSYNCTITLE', 'Update the CMS database entries of files on the filesystem. Useful when new files have been uploaded outside of the CMS, e.g. through FTP.'),
$this->Link('doSync'),
_t('AssetAdmin.FILESYSTEMSYNC','Sync files')
)
);
} else {
$syncButton = null;
}
// Move existing fields to a "details" tab, unless they've already been tabbed out through extensions.
// Required to keep Folder->getCMSFields() simple and reuseable,
// without any dependencies into AssetAdmin (e.g. useful for "add folder" views).
if(!$fields->hasTabset()) {
$tabs = new TabSet('Root',
$tabList = new Tab('ListView', _t('AssetAdmin.ListView', 'List View')),
$tabTree = new Tab('TreeView', _t('AssetAdmin.TreeView', 'Tree View'))
);
$tabList->addExtraClass("content-listview cms-tabset-icon list");
$tabTree->addExtraClass("content-treeview cms-tabset-icon tree");
if($fields->Count() && $folder->exists()) {
$tabs->push($tabDetails = new Tab('DetailsView', _t('AssetAdmin.DetailsView', 'Details')));
$tabDetails->addExtraClass("content-galleryview cms-tabset-icon edit");
foreach($fields as $field) {
$fields->removeByName($field->getName());
$tabDetails->push($field);
}
}
$fields->push($tabs);
}
// we only add buttons if they're available. User might not have permission and therefore
// the button shouldn't be available. Adding empty values into a ComposteField breaks template rendering.
$actionButtonsComposite = CompositeField::create()->addExtraClass('cms-actions-row');
if($uploadBtn) $actionButtonsComposite->push($uploadBtn);
if($addFolderBtn) $actionButtonsComposite->push($addFolderBtn);
if($syncButton) $actionButtonsComposite->push($syncButton);
// List view
$fields->addFieldsToTab('Root.ListView', array(
$actionsComposite = CompositeField::create(
$actionButtonsComposite
)->addExtraClass('cms-content-toolbar field'),
$gridField
));
$treeField = new LiteralField('Tree', '');
// Tree view
$fields->addFieldsToTab('Root.TreeView', array(
clone $actionsComposite,
// TODO Replace with lazy loading on client to avoid performance hit of rendering potentially unused views
new LiteralField(
'Tree',
FormField::create_tag(
'div',
array(
'class' => 'cms-tree',
'data-url-tree' => $this->Link('getsubtree'),
'data-url-savetreenode' => $this->Link('savetreenode')
),
$this->SiteTreeAsUL()
)
)
));
// Move actions to "details" tab (they don't make sense on list/tree view)
$actions = $form->Actions();
$saveBtn = $actions->fieldByName('action_save');
$deleteBtn = $actions->fieldByName('action_delete');
$actions->removeByName('action_save');
$actions->removeByName('action_delete');
if(($saveBtn || $deleteBtn) && $fields->fieldByName('Root.DetailsView')) {
$fields->addFieldToTab(
'Root.DetailsView',
CompositeField::create($saveBtn,$deleteBtn)->addExtraClass('Actions')
);
}
$fields->setForm($form);
$form->setTemplate($this->getTemplatesWithSuffix('_EditForm'));
// TODO Can't merge $FormAttributes in template at the moment
$form->addExtraClass('cms-edit-form cms-panel-padded center ' . $this->BaseCSSClasses());
$form->setAttribute('data-pjax-fragment', 'CurrentForm');
$form->Fields()->findOrMakeTab('Root')->setTemplate('CMSTabSet');
$this->extend('updateEditForm', $form);
return $form;
}
public function addfolder($request) {
$obj = $this->customise(array(
'EditForm' => $this->AddForm()
));
if($request->isAjax()) {
// Rendering is handled by template, which will call EditForm() eventually
$content = $obj->renderWith($this->getTemplatesWithSuffix('_Content'));
} else {
$content = $obj->renderWith($this->getViewer('show'));
}
return $content;
}
public function delete($data, $form) {
$className = $this->stat('tree_class');
$record = DataObject::get_by_id($className, Convert::raw2sql($data['ID']));
if($record && !$record->canDelete()) return Security::permissionFailure();
if(!$record || !$record->ID) throw new HTTPResponse_Exception("Bad record ID #" . (int)$data['ID'], 404);
$parentID = $record->ParentID;
$record->delete();
$this->setCurrentPageID(null);
$this->response->addHeader('X-Status', rawurlencode(_t('LeftAndMain.DELETED', 'Deleted.')));
$this->response->addHeader('X-Pjax', 'Content');
return $this->redirect(Controller::join_links($this->Link('show'), $parentID ? $parentID : 0));
}
/**
* Get the search context
*
* #return SearchContext
*/
public function getSearchContext() {
$context = singleton('File')->getDefaultSearchContext();
// Namespace fields, for easier detection if a search is present
foreach($context->getFields() as $field) $field->setName(sprintf('q[%s]', $field->getName()));
foreach($context->getFilters() as $filter) $filter->setFullName(sprintf('q[%s]', $filter->getFullName()));
// Customize fields
$context->addField(
new HeaderField('q[Date]', _t('CMSSearch.FILTERDATEHEADING', 'Date'), 4)
);
$context->addField(
DateField::create(
'q[CreatedFrom]',
_t('CMSSearch.FILTERDATEFROM', 'From')
)->setConfig('showcalendar', true)
);
$context->addField(
DateField::create(
'q[CreatedTo]',
_t('CMSSearch.FILTERDATETO', 'To')
)->setConfig('showcalendar', true)
);
$appCategories = array(
'image' => _t('AssetAdmin.AppCategoryImage', 'Image'),
'audio' => _t('AssetAdmin.AppCategoryAudio', 'Audio'),
'mov' => _t('AssetAdmin.AppCategoryVideo', 'Video'),
'flash' => _t('AssetAdmin.AppCategoryFlash', 'Flash', 'The fileformat'),
'zip' => _t('AssetAdmin.AppCategoryArchive', 'Archive', 'A collection of files'),
'doc' => _t('AssetAdmin.AppCategoryDocument', 'Document')
);
$context->addField(
$typeDropdown = new DropdownField(
'q[AppCategory]',
_t('AssetAdmin.Filetype', 'File type'),
$appCategories
)
);
$typeDropdown->setEmptyString(' ');
$context->addField(
new CheckboxField('q[CurrentFolderOnly]', _t('AssetAdmin.CurrentFolderOnly', 'Limit to current folder?'))
);
$context->getFields()->removeByName('q[Title]');
return $context;
}
/**
* Returns a form for filtering of files and assets gridfield.
* Result filtering takes place in {#link getList()}.
*
* #return Form
* #see AssetAdmin.js
*/
public function SearchForm() {
$folder = $this->currentPage();
$context = $this->getSearchContext();
$fields = $context->getSearchFields();
$actions = new FieldList(
FormAction::create('doSearch', _t('CMSMain_left_ss.APPLY_FILTER', 'Apply Filter'))
->addExtraClass('ss-ui-action-constructive'),
Object::create('ResetFormAction', 'clear', _t('CMSMain_left_ss.RESET', 'Reset'))
);
$form = new Form($this, 'filter', $fields, $actions);
$form->setFormMethod('GET');
$form->setFormAction(Controller::join_links($this->Link('show'), $folder->ID));
$form->addExtraClass('cms-search-form');
$form->loadDataFrom($this->request->getVars());
$form->disableSecurityToken();
// This have to match data-name attribute on the gridfield so that the javascript selectors work
$form->setAttribute('data-gridfield', 'File');
return $form;
}
public function AddForm() {
$folder = singleton('Folder');
$form = CMSForm::create(
$this,
'AddForm',
new FieldList(
new TextField("Name", _t('File.Name')),
new HiddenField('ParentID', false, $this->request->getVar('ParentID'))
),
new FieldList(
FormAction::create('doAdd', _t('AssetAdmin_left_ss.GO','Go'))
->addExtraClass('ss-ui-action-constructive')->setAttribute('data-icon', 'accept')
->setTitle(_t('AssetAdmin.ActionAdd', 'Add folder'))
)
)->setHTMLID('Form_AddForm');
$form->setResponseNegotiator($this->getResponseNegotiator());
$form->setTemplate($this->getTemplatesWithSuffix('_EditForm'));
// TODO Can't merge $FormAttributes in template at the moment
$form->addExtraClass('add-form cms-add-form cms-edit-form cms-panel-padded center ' . $this->BaseCSSClasses());
return $form;
}
/**
* Add a new group and return its details suitable for ajax.
*
* #todo Move logic into Folder class, and use LeftAndMain->doAdd() default implementation.
*/
public function doAdd($data, $form) {
$class = $this->stat('tree_class');
// check create permissions
if(!singleton($class)->canCreate()) return Security::permissionFailure($this);
// check addchildren permissions
if(
singleton($class)->hasExtension('Hierarchy')
&& isset($data['ParentID'])
&& is_numeric($data['ParentID'])
&& $data['ParentID']
) {
$parentRecord = DataObject::get_by_id($class, $data['ParentID']);
if(
$parentRecord->hasMethod('canAddChildren')
&& !$parentRecord->canAddChildren()
) return Security::permissionFailure($this);
} else {
$parentRecord = null;
}
$parent = (isset($data['ParentID']) && is_numeric($data['ParentID'])) ? (int)$data['ParentID'] : 0;
$name = (isset($data['Name'])) ? basename($data['Name']) : _t('AssetAdmin.NEWFOLDER',"NewFolder");
if(!$parentRecord || !$parentRecord->ID) $parent = 0;
// Get the folder to be created
if($parentRecord && $parentRecord->ID) $filename = $parentRecord->FullPath . $name;
else $filename = ASSETS_PATH . '/' . $name;
// Actually create
if(!file_exists(ASSETS_PATH)) {
mkdir(ASSETS_PATH);
}
$record = new Folder();
$record->ParentID = $parent;
$record->Name = $record->Title = basename($filename);
// Ensure uniqueness
$i = 2;
$baseFilename = substr($record->Filename, 0, -1) . '-';
while(file_exists($record->FullPath)) {
$record->Filename = $baseFilename . $i . '/';
$i++;
}
$record->Name = $record->Title = basename($record->Filename);
$record->write();
mkdir($record->FullPath);
chmod($record->FullPath, Filesystem::config()->file_create_mask);
if($parentRecord) {
return $this->redirect(Controller::join_links($this->Link('show'), $parentRecord->ID));
} else {
return $this->redirect($this->Link());
}
}
/**
* Custom currentPage() method to handle opening the 'root' folder
*/
public function currentPage() {
$id = $this->currentPageID();
if($id && is_numeric($id) && $id > 0) {
$folder = DataObject::get_by_id('Folder', $id);
if($folder && $folder->exists()) {
return $folder;
}
}
$this->setCurrentPageID(null);
return new Folder();
}
public function getSiteTreeFor($className, $rootID = null, $childrenMethod = null, $numChildrenMethod = null, $filterFunction = null, $minNodeCount = 30) {
if (!$childrenMethod) $childrenMethod = 'ChildFolders';
if (!$numChildrenMethod) $numChildrenMethod = 'numChildFolders';
return parent::getSiteTreeFor($className, $rootID, $childrenMethod, $numChildrenMethod, $filterFunction, $minNodeCount);
}
public function getCMSTreeTitle() {
return Director::absoluteBaseURL() . "assets";
}
public function SiteTreeAsUL() {
return $this->getSiteTreeFor($this->stat('tree_class'), null, 'ChildFolders', 'numChildFolders');
}
//------------------------------------------------------------------------------------------//
// Data saving handlers
/**
* Can be queried with an ajax request to trigger the filesystem sync. It returns a FormResponse status message
* to display in the CMS
*/
public function doSync() {
$message = Filesystem::sync();
$this->response->addHeader('X-Status', rawurlencode($message));
return;
}
/**
* #################################
* Garbage collection.
* #################################
*/
/**
* Removes all unused thumbnails from the file store
* and returns the status of the process to the user.
*/
public function deleteunusedthumbnails($request) {
// Protect against CSRF on destructive action
if(!SecurityToken::inst()->checkRequest($request)) return $this->httpError(400);
$count = 0;
$thumbnails = $this->getUnusedThumbnails();
if($thumbnails) {
foreach($thumbnails as $thumbnail) {
unlink(ASSETS_PATH . "/" . $thumbnail);
$count++;
}
}
$message = _t(
'AssetAdmin.THUMBSDELETED',
'{count} unused thumbnails have been deleted',
array('count' => $count)
);
$this->response->addHeader('X-Status', rawurlencode($message));
return;
}
/**
* Creates array containg all unused thumbnails.
*
* Array is created in three steps:
* 1. Scan assets folder and retrieve all thumbnails
* 2. Scan all HTMLField in system and retrieve thumbnails from them.
* 3. Count difference between two sets (array_diff)
*
* #return array
*/
private function getUnusedThumbnails() {
$allThumbnails = array();
$usedThumbnails = array();
$dirIterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator(ASSETS_PATH));
$classes = ClassInfo::subclassesFor('SiteTree');
if($dirIterator) {
foreach($dirIterator as $file) {
if($file->isFile()) {
if(strpos($file->getPathname(), '_resampled') !== false) {
$pathInfo = pathinfo($file->getPathname());
if(in_array(strtolower($pathInfo['extension']), array('jpeg', 'jpg', 'jpe', 'png', 'gif'))) {
$path = str_replace('\\','/', $file->getPathname());
$allThumbnails[] = substr($path, strpos($path, '/assets/') + 8);
}
}
}
}
}
if($classes) {
foreach($classes as $className) {
$SNG_class = singleton($className);
$objects = DataObject::get($className);
if($objects !== NULL) {
foreach($objects as $object) {
foreach($SNG_class->db() as $fieldName => $fieldType) {
if($fieldType == 'HTMLText') {
$url1 = HTTP::findByTagAndAttribute($object->$fieldName,array('img' => 'src'));
if($url1 != NULL) {
$usedThumbnails[] = substr($url1[0], strpos($url1[0], '/assets/') + 8);
}
if($object->latestPublished > 0) {
$object = Versioned::get_latest_version($className, $object->ID);
$url2 = HTTP::findByTagAndAttribute($object->$fieldName, array('img' => 'src'));
if($url2 != NULL) {
$usedThumbnails[] = substr($url2[0], strpos($url2[0], '/assets/') + 8);
}
}
}
}
}
}
}
}
return array_diff($allThumbnails, $usedThumbnails);
}
/**
* #param bool $unlinked
* #return ArrayList
*/
public function Breadcrumbs($unlinked = false) {
$items = parent::Breadcrumbs($unlinked);
// The root element should explicitly point to the root node.
// Uses session state for current record otherwise.
$items[0]->Link = Controller::join_links(singleton('AssetAdmin')->Link('show'), 0);
// If a search is in progress, don't show the path
if($this->request->requestVar('q')) {
$items = $items->limit(1);
$items->push(new ArrayData(array(
'Title' => _t('LeftAndMain.SearchResults', 'Search Results'),
'Link' => Controller::join_links($this->Link(), '?' . http_build_query(array('q' => $this->request->requestVar('q'))))
)));
}
// If we're adding a folder, note that in breadcrumbs as well
if($this->request->param('Action') == 'addfolder') {
$items->push(new ArrayData(array(
'Title' => _t('Folder.AddFolderButton', 'Add folder'),
'Link' => false
)));
}
return $items;
}
public function providePermissions() {
$title = _t("AssetAdmin.MENUTITLE", LeftAndMain::menu_title_for_class($this->class));
return array(
"CMS_ACCESS_AssetAdmin" => array(
'name' => _t('CMSMain.ACCESS', "Access to '{title}' section", array('title' => $title)),
'category' => _t('Permission.CMS_ACCESS_CATEGORY', 'CMS Access')
)
);
}
}
/**
* Delete multiple {#link Folder} records (and the associated filesystem nodes).
* Usually used through the {#link AssetAdmin} interface.
*
* #package cms
* #subpackage batchactions
*/
class AssetAdmin_DeleteBatchAction extends CMSBatchAction {
public function getActionTitle() {
// _t('AssetAdmin_left_ss.SELECTTODEL','Select the folders that you want to delete and then click the button below')
return _t('AssetAdmin_DeleteBatchAction.TITLE', 'Delete folders');
}
public function run(SS_List $records) {
$status = array(
'modified'=>array(),
'deleted'=>array()
);
foreach($records as $record) {
$id = $record->ID;
// Perform the action
if($record->canDelete()) $record->delete();
$status['deleted'][$id] = array();
$record->destroy();
unset($record);
}
return Convert::raw2json($status);
}
}
Related
How can i use all the data collected and by click of button have it inserted into an email template
How can i use the data that will be stored when entered and on a click of a button certain fields will automatically fiilled in to an email template. Array ( 'bookingsid', 'vtiger_bookings', 'bookingsid' ),); /** * Mandatory table for supporting custom fields. */ var $customFieldTable = Array('vtiger_bookingscf', 'bookingsid'); /** * Mandatory for Saving, Include tables related to this module. */ var $tab_name = Array('vtiger_crmentity', 'vtiger_bookings', 'vtiger_bookingscf'); /** * Mandatory for Saving, Include tablename and tablekey columnname here. */ var $tab_name_index = Array( 'vtiger_crmentity' => 'crmid', 'vtiger_bookings' => 'bookingsid', 'vtiger_bookingscf'=>'bookingsid'); /** * Mandatory for Listing (Related listview) */ var $list_fields = Array ( /* Format: Field Label => Array(tablename, columnname) */ // tablename should not have prefix 'vtiger_' 'Name' => Array('bookings', 'name'), 'Assigned To' => Array('crmentity','smownerid') ); var $list_fields_name = Array ( /* Format: Field Label => fieldname */ 'Name' => 'name', 'Assigned To' => 'assigned_user_id', ); // Make the field link to detail view var $list_link_field = 'name'; // For Popup listview and UI type support var $search_fields = Array( /* Format: Field Label => Array(tablename, columnname) */ // tablename should not have prefix 'vtiger_' 'Name' => Array('bookings', 'name'), 'Assigned To' => Array('vtiger_crmentity','assigned_user_id'), ); var $search_fields_name = Array ( /* Format: Field Label => fieldname */ 'Name' => 'name', 'Assigned To' => 'assigned_user_id', ); // For Popup window record selection var $popup_fields = Array ('name'); // For Alphabetical search var $def_basicsearch_col = 'name'; // Column value to use on detail view record text display var $def_detailview_recname = 'name'; // Used when enabling/disabling the mandatory fields for the module. // Refers to vtiger_field.fieldname values. var $mandatory_fields = Array('name','assigned_user_id'); var $default_order_by = 'name'; var $default_sort_order='ASC'; function Bookings() { $this->db = PearDatabase::getInstance(); } /** * Invoked when special actions are performed on the module. * #param String Module name * #param String Event Type */ function vtlib_handler($moduleName, $eventType) { global $adb; if($eventType == 'module.postinstall') { // TODO Handle actions after this module is installed. $this->init($moduleName); } else if($eventType == 'module.disabled') { // TODO Handle actions before this module is being uninstalled. } else if($eventType == 'module.enabled') { // TODO Handle actions before this module is being uninstalled. } else if($eventType == 'module.preuninstall') { // TODO Handle actions when this module is about to be deleted. } else if($eventType == 'module.preupdate') { // TODO Handle actions before this module is updated. } else if($eventType == 'module.postupdate') { // TODO Handle actions after this module is updated. } } function get_emails($id, $cur_tab_id, $rel_tab_id, $actions=false) { global $currentModule; $related_module = vtlib_getModuleNameById($rel_tab_id); require_once("modules/$related_module/$related_module.php"); $other = new $related_module(); vtlib_setup_modulevars($related_module, $other); $returnset = '&return_module='.$currentModule.'&return_action=CallRelatedList&return_id='.$id; $button = ''; $userNameSql = getSqlForNameInDisplayFormat(array('first_name'=>'vtiger_users.first_name', 'last_name' => 'vtiger_users.last_name'), 'Users'); $query = "SELECT CASE WHEN (vtiger_users.user_name NOT LIKE '') THEN $userNameSql ELSE vtiger_groups.groupname END AS user_name, vtiger_activity.activityid, vtiger_activity.subject, vtiger_activity.activitytype, vtiger_crmentity.modifiedtime, vtiger_crmentity.crmid, vtiger_crmentity.smownerid, vtiger_activity.date_start, vtiger_activity.time_start, vtiger_seactivityrel.crmid as parent_id FROM vtiger_activity, vtiger_seactivityrel, vtiger_bookings, vtiger_users, vtiger_crmentity LEFT JOIN vtiger_groups ON vtiger_groups.groupid = vtiger_crmentity.smownerid WHERE vtiger_seactivityrel.activityid = vtiger_activity.activityid AND vtiger_bookings.bookingsid = vtiger_seactivityrel.crmid AND vtiger_users.id = vtiger_crmentity.smownerid AND vtiger_crmentity.crmid = vtiger_activity.activityid AND vtiger_bookings.bookingsid = $id AND vtiger_activity.activitytype = 'Emails' AND vtiger_crmentity.deleted = 0"; $return_value = GetRelatedList($currentModule, $related_module, $other, $query, $button, $returnset); if($return_value == null) $return_value = Array(); $return_value['CUSTOM_BUTTON'] = $button; return $return_value; } /** * When install module * #param $moduleName */ public function init($moduleName) { $module = Vtiger_Module::getInstance($moduleName); // Enable Activities $activityFieldTypeId = 34; $this->addModuleRelatedToForEvents($module->name, $activityFieldTypeId); // Enable ModTracker require_once 'modules/ModTracker/ModTracker.php'; ModTracker::enableTrackingForModule($module->id); // Enable Comments $commentInstance = Vtiger_Module::getInstance('ModComments'); $commentRelatedToFieldInstance = Vtiger_Field::getInstance('related_to', $commentInstance); $commentRelatedToFieldInstance->setRelatedModules(array($module->name)); // Customize Record Numbering $prefix = 'NO'; if (strlen($module->name) >= 2) { $prefix = substr($module->name, 0, 2); $prefix = strtoupper($prefix); } $this->customizeRecordNumbering($module->name, $prefix, 1); } /** * #param string $moduleName * #param int $fieldTypeId */ public function addModuleRelatedToForEvents($moduleName, $fieldTypeId) { global $adb; $sqlCheckProject = "SELECT * FROM `vtiger_ws_referencetype` WHERE fieldtypeid = ? AND type = ?"; $rsCheckProject = $adb->pquery($sqlCheckProject, array($fieldTypeId, $moduleName)); if ($adb->num_rows($rsCheckProject) pquery("INSERT INTO `vtiger_ws_referencetype` (`fieldtypeid`, `type`) VALUES (?, ?)", array($fieldTypeId, $moduleName)); } } /** * #param string $sourceModule * #param string $prefix * #param int $sequenceNumber * #return array */ public function customizeRecordNumbering($sourceModule, $prefix = 'NO', $sequenceNumber = 1) { $moduleModel = Settings_Vtiger_CustomRecordNumberingModule_Model::getInstance($sourceModule); $moduleModel->set('prefix', $prefix); $moduleModel->set('sequenceNumber', $sequenceNumber); $result = $moduleModel->setModuleSequence(); return $result; } /** * Save the related module record information. Triggered from CRMEntity->saveentity method or updateRelations.php * #param String This module name * #param Integer This module record number * #param String Related module name * #param mixed Integer or Array of related module record number */ function save_related_module($module, $crmid, $with_module, $with_crmids) { $adb = PearDatabase::getInstance(); if(!is_array($with_crmids)) $with_crmids = Array($with_crmids); foreach($with_crmids as $with_crmid) { if($with_module == 'Calendar') { $checkpresence = $adb->pquery("SELECT crmid FROM vtiger_seactivityrel WHERE crmid = ? AND activityid = ?", Array($crmid, $with_crmids)); // Relation already exists? No need to add again if ($checkpresence && $adb->num_rows($checkpresence)) continue; $adb->pquery("INSERT INTO vtiger_seactivityrel(crmid, activityid) VALUES(?,?)", array($crmid, $with_crmids)); }else { parent::save_related_module($module, $crmid, $with_module, $with_crmid); } } } }
Moodle: Error on form submission in activity module
Below is a sample of the code that I'm using for the form and then the handling og the file saving. There's something that I'm doing wrong, but I'm not sure what it is. Please assist. I have attached a screenshot showing the error that I'm getting after submitting the form. error on form submission in moodle inside activity module class mod_audio_mod_form extends moodleform_mod { /** * Defines forms elements */ public function definition() { global $CFG; $mform = $this->_form; // Adding the "general" fieldset, where all the common settings are showed. $mform->addElement('header', 'general', get_string('general', 'form')); // Adding the standard "name" field. $mform->addElement('text', 'name', get_string('audioname', 'audio'), array('size' => '64')); if (!empty($CFG->formatstringstriptags)) { $mform->setType('name', PARAM_TEXT); } else { $mform->setType('name', PARAM_CLEANHTML); } $mform->addRule('name', null, 'required', null, 'client'); $mform->addRule('name', get_string('maximumchars', '', 255), 'maxlength', 255, 'client'); $mform->addHelpButton('name', 'audioname', 'audio'); // Adding the standard "intro" and "introformat" fields. if ($CFG->branch >= 29) { $this->standard_intro_elements(); } else { $this->add_intro_editor(); } // Adding the rest of audio settings, spreading all them into this fieldset // ... or adding more fieldsets ('header' elements) if needed for better logic. $mform->addElement('header', 'audiofieldset', get_string('audiofieldset', 'audio')); $filemanager_options = array(); $filemanager_options['accepted_types'] = '*'; $filemanager_options['maxbytes'] = 0; $filemanager_options['maxfiles'] = -1; $filemanager_options['mainfile'] = true; $mform->addElement('filemanager', 'files', get_string('selectfiles'), null, $filemanager_options); $mform->addRule('files', null, 'required', null, 'client'); // Add standard grading elements. $this->standard_grading_coursemodule_elements(); // Add standard elements, common to all modules. $this->standard_coursemodule_elements(); // Add standard buttons, common to all modules. $this->add_action_buttons(); } function data_preprocessing(&$default_values) { if ($this->current->instance) { // editing existing instance - copy existing files into draft area $draftitemid = file_get_submitted_draft_itemid('files'); file_prepare_draft_area($draftitemid, $this->context->id, 'mod_audio', 'content', 0, array('subdirs' => true)); $default_values['files'] = $draftitemid; } } function definition_after_data() { if ($this->current->instance and $this->current->tobemigrated) { // resource not migrated yet return; } parent::definition_after_data(); } function validation($data, $files) { global $USER; $errors = parent::validation($data, $files); $usercontext = context_user::instance($USER->id); $fs = get_file_storage(); if (!$files = $fs->get_area_files($usercontext->id, 'user', 'draft', $data['files'], 'sortorder, id', false)) { $errors['files'] = get_string('required'); return $errors; } if (count($files) == 1) { // no need to select main file if only one picked return $errors; } else if (count($files) > 1) { $mainfile = false; foreach ($files as $file) { if ($file->get_sortorder() == 1) { $mainfile = true; break; } } // set a default main file if (!$mainfile) { $file = reset($files); file_set_sortorder($file->get_contextid(), $file->get_component(), $file->get_filearea(), $file->get_itemid(), $file->get_filepath(), $file->get_filename(), 1); } } return $errors; } } // THE CLASS IN LOCALLIB.PHP class audio_content_file_info extends file_info_stored { public function get_parent() { if ($this->lf->get_filepath() === '/' and $this->lf->get_filename() === '.') { return $this->browser->get_file_info($this->context); } return parent::get_parent(); } public function get_visible_name() { if ($this->lf->get_filepath() === '/' and $this->lf->get_filename() === '.') { return $this->topvisiblename; } return parent::get_visible_name(); } function save_files($data) { global $DB; // Storage of files from the filemanager (videos). $draftitemid = $data->files; if ($draftitemid) { file_save_draft_area_files( $draftitemid, $this->context->id, 'mod_audio', 'files', 0 ); } } } // FUNCTION THAT ADDS THE INSTANCE IN LIB.PHP function audio_add_instance(stdClass $audio, mod_audio_mod_form $mform = null) { global $CFG, $DB; require_once("$CFG->libdir/resourcelib.php"); require_once("$CFG->dirroot/mod/audio/locallib.php"); $cmid = $audio->coursemodule; $audio->timecreated = time(); $audio->timemodified = time(); resource_set_display_options($audio); $audio->id = $DB->insert_record('audio', $audio); $context = context_module::instance($audio->coursemodule); $audiofile = new audio_content_file_info($context, null, null); $audiofile->save_files($audio); // we need to use context now, so we need to make sure all needed info is already in db $DB->set_field('course_modules', 'instance', $audio->id, array('id' => $cmid)); resource_set_mainfile($audio); return $audio->id; } Please advice on what I could be doing wrong here.
How to generate a custom CSV export?
I have a page called EventPage that I am managing via a Model admin. Also using Catalog manager: https://github.com/littlegiant/silverstripe-catalogmanager Question is I need to be able to export all the expired events (And all of the fields). I have an 'EndDate' => 'Date', field on the EventPage. So I want to only show EventPages in my CSV export where the EndDate is GreaterThanOrEqual to todays date e.g Expired. The following generates an CSV export button, but currently it is exporting all the fields, where as I want to filter it so we only show the expired events. How do I go about this? <?php class EventAdmin extends CatalogPageAdmin { public $showImportForm = false; private static $managed_models = array( 'EventPage', 'EventCategory', 'EventSubmission', ); private static $url_segment = 'events'; private static $menu_title = 'Events'; public function getEditForm($id = null, $fields = null) { $form = parent::getEditForm($id, $fields); $gridFieldName = 'EventPage'; $gridField = $form->Fields()->fieldByName($gridFieldName); if ($gridField) { $gridField->getConfig()->addComponent(new GridFieldExportButton()); } return $form; } }
We can create a custom export button to filter the list of items before exporting them. First we create a GridFieldExportExpiredEventsButton which extends GridFieldExportButton. This is a complete copy of the current SilverStripe 3.5 generateExportFileData function but with an added filterByCallback on the $items list to filter items that have an EndDate < date('Y-m-d'). class GridFieldExportExpiredEventsButton extends GridFieldExportButton { public function getHTMLFragments($gridField) { $button = new GridField_FormAction( $gridField, 'export', 'Export expired events', 'export', null ); $button->setAttribute('data-icon', 'download-csv'); $button->addExtraClass('no-ajax action_export'); $button->setForm($gridField->getForm()); return array( $this->targetFragment => '<p class="grid-csv-button">' . $button->Field() . '</p>', ); } public function generateExportFileData($gridField) { $separator = $this->csvSeparator; $csvColumns = $this->getExportColumnsForGridField($gridField); $fileData = ''; $member = Member::currentUser(); if($this->csvHasHeader) { $headers = array(); // determine the CSV headers. If a field is callable (e.g. anonymous function) then use the // source name as the header instead foreach($csvColumns as $columnSource => $columnHeader) { $headers[] = (!is_string($columnHeader) && is_callable($columnHeader)) ? $columnSource : $columnHeader; } $fileData .= "\"" . implode("\"{$separator}\"", array_values($headers)) . "\""; $fileData .= "\n"; } //Remove GridFieldPaginator as we're going to export the entire list. $gridField->getConfig()->removeComponentsByType('GridFieldPaginator'); $items = $gridField->getManipulatedList(); $items = $items->filterByCallback(function($item) { // The following line modifies what items are filtered. Change this to change what items are filtered return $item->EndDate < date('Y-m-d'); }); // #todo should GridFieldComponents change behaviour based on whether others are available in the config? foreach($gridField->getConfig()->getComponents() as $component){ if($component instanceof GridFieldFilterHeader || $component instanceof GridFieldSortableHeader) { $items = $component->getManipulatedData($gridField, $items); } } foreach($items->limit(null) as $item) { if(!$item->hasMethod('canView') || $item->canView($member)) { $columnData = array(); foreach($csvColumns as $columnSource => $columnHeader) { if(!is_string($columnHeader) && is_callable($columnHeader)) { if($item->hasMethod($columnSource)) { $relObj = $item->{$columnSource}(); } else { $relObj = $item->relObject($columnSource); } $value = $columnHeader($relObj); } else { $value = $gridField->getDataFieldValue($item, $columnSource); if($value === null) { $value = $gridField->getDataFieldValue($item, $columnHeader); } } $value = str_replace(array("\r", "\n"), "\n", $value); $columnData[] = '"' . str_replace('"', '""', $value) . '"'; } $fileData .= implode($separator, $columnData); $fileData .= "\n"; } if($item->hasMethod('destroy')) { $item->destroy(); } } return $fileData; } } The extra line that we have added that filters the export items is: return $item->EndDate < date('Y-m-d'); Alter this to alter the list of items that are exported. I have set this to only return items which have an EndDate that is in the past. Change this as you need. We then add this export button to our grid field in our event model admin: class EventAdmin extends CatalogPageAdmin { private static $managed_models = array( 'EventPage' ); public function getEditForm($id = null, $fields = null) { $form = parent::getEditForm($id); if ($this->modelClass == 'EventPage') { $gridField = $form->Fields()->fieldByName($this->modelClass); $gridField->getConfig()->removeComponentsByType('GridFieldExportButton'); $gridField->getConfig()->addComponent(new GridFieldExportExpiredEventsButton('buttons-before-left')); } return $form; } }
This was originally two answers... To limit the fields Have you had a look at the GridFieldExportButton class ? The constructor /** * #param string $targetFragment The HTML fragment to write the button into * #param array $exportColumns The columns to include in the export */ public function __construct($targetFragment = "after", $exportColumns = null) { $this->targetFragment = $targetFragment; $this->exportColumns = $exportColumns; } So you should be able to pass $exportColumns as an argument. in your code that would be if ($gridField) { $gridField->getConfig()->addComponent(new GridFieldExportButton("after", ["field1", "field2"])); } OR - EVEN BETTER SOLUTION you can define your summary fields on EventPage as such private static $summary_fields = ["FIeld1", "Field2"]; Then make sure your flush and it should use that as fields. To filter which items to export in your CSV So in this case, I think you should create a new class that extends GridFieldExportButton (maybe called EventPageCSVExportButton or something) and override the methods you want. In your case it would probably be generateExportFileData(), just do a check in the loop and exclude data you don't want. Then use that new class in your EventAdmin.
Have you had a look at the GridFieldExportButton class ? The constructor /** * #param string $targetFragment The HTML fragment to write the button into * #param array $exportColumns The columns to include in the export */ public function __construct($targetFragment = "after", $exportColumns = null) { $this->targetFragment = $targetFragment; $this->exportColumns = $exportColumns; } So you should be able to pass $exportColumns as an argument. in your code that would be if ($gridField) { $gridField->getConfig()->addComponent(new GridFieldExportButton("after", ["field1", "field2"])); } OR - EVEN BETTER SOLUTION you can define your summary fields on EventPage as such private static $summary_fields = ["FIeld1", "Field2"]; Then make sure your flush and it should use that as fields.
foreach() , array_walk() in the script
I have work to used this script in my centos server, but when i try to execute it , i have this Problems Line's. [27-Nov-2016 14:37:15 UTC] PHP Warning: array_walk() expects parameter 1 to be array, boolean given in /root/facebook-live-reactions/src/Facebook.php on line 48 [27-Nov-2016 14:37:15 UTC] PHP Warning: Invalid argument supplied for foreach() in /root/facebook-live-reactions/src/Renderer.php on line 22 This is the file what he have problem >> Renderer.php <?php namespace FBReactions; class Renderer { private $image; private $settings; public function __construct($image, $settings) { $this->image = $image; $this->settings = $settings; } /* * Adds reaction counts to an image */ public function drawReactionCount($reactions) { $reactionSettings = $this->settings['REACTION_SETTINGS']; foreach ($reactions as $index => $reactionCount) { $this->image->text( $reactionCount, $reactionSettings['REACTIONS'][$index]['XPOS'], $reactionSettings['REACTIONS'][$index]['YPOS'], function ($font) use ($reactionSettings) { $font->file($reactionSettings['FONT']['FAMILY']); $font->size($reactionSettings['FONT']['SIZE']); $font->color($reactionSettings['FONT']['COLOR']); $font->align('center'); } ); } } /* * Draws the shoutout text to image */ public function drawShoutout($user, $shoutout) { $shout = "#{$user}, {$shoutout}"; $this->image->text( $shout, $this->settings['SHOUTOUT_SETTINGS']['SHOUTOUT_TEXT']['XPOS'], $this->settings['SHOUTOUT_SETTINGS']['SHOUTOUT_TEXT']['YPOS'], function ($font) { $font->file($this->settings['SHOUTOUT_SETTINGS']['SHOUTOUT_TEXT']['FONT']['FAMILY']); $font->size($this->settings['SHOUTOUT_SETTINGS']['SHOUTOUT_TEXT']['FONT']['SIZE']); $font->color($this->settings['SHOUTOUT_SETTINGS']['SHOUTOUT_TEXT']['FONT']['COLOR']); $font->align('left'); } ); } /* * Draws the profile image onto the shoutbox */ public function drawProfileImage($filename, $xpos, $ypos) { $this->image->insert($filename, 'bottom-left', $xpos, $ypos); } /* * I don't like this! Might be better to return $this->image instead */ public function save($filename) { return $this->image->save($filename); } } Facebook.php Code: <?php namespace FBReactions; class Facebook { private $fb; private $accessToken; public function __construct($fb, $accessToken) { $this->fb = $fb; $this->accessToken = $accessToken; } public function reactionCount($objectID, $reactions) { foreach ($reactions as $key => $position) { $fields[] = "reactions.type({$key}).limit(0).summary(total_count).as({$key})"; } $reactionParams = ['ids' => $objectID, 'fields' => join(',', $fields)]; $endpoint = '/?' . http_build_query($reactionParams); $request = $this->fb->request('GET', $endpoint, [], $this->accessToken); /* * Fetch the reactions count from Facebook */ try { $response = $this->fb->getClient()->sendRequest($request); $reactions = json_decode($response->getBody(), true); $reactions = current($reactions); } catch (\Exception $e) { // die('Error getting reactions: ' . $e); return []; } /* * We don't need the id element. Remove it. */ unset($reactions['id']); /* * We're only interested in the reaction count */ array_walk($reactions, function(&$reaction) { $reaction = $reaction['summary']['total_count']; }); return $reactions; } /* * Fetch latest comments */ function comments($objectID) { $commentParams = ['filter' => 'stream', 'order' => 'reverse_chronological']; $request = $this->fb->request( 'GET', "/{$objectID}/comments?" . http_build_query($commentParams), [], $this->accessToken ); try { $response = $this->fb->getClient()->sendRequest($request); return json_decode($response->getBody(), true)['data']; } catch (\Exception $e) { // die('Error getting comments: ' . $e); return []; } } /* * Returns an array of comments that contains a specific keyword */ function commentsByKeyword($objectID, $keyword, $caseSensitive = false) { $comments = $this->comments($objectID); return array_filter($comments, function ($comment) use ($keyword, $caseSensitive) { $message = $comment['message']; if ($caseSensitive) { return strpos($message, $keyword) > -1; } else { return strpos(strtolower($message), strtolower($keyword)) > -1; } }); } /* * Downloads and saves public profile image */ function saveProfileImage($uid, $width, $height, $filename) { $profileImageParams = [ 'width' => $width, 'height' => $height, ]; $endpoint = "http://graph.facebook.com/{$uid}/picture?" . http_build_query($profileImageParams); copy($endpoint, $filename); } } i well be thankful if any one help me in this case, i work in it two day's without any solution , i try many way to fixed it.. Thank you
You do current in Facebook.php, line 32: $reactions = current($reactions); It might the place when you get boolean instead of array: if $reactions array is empty current() method will return false and you will get a warning 'array_walk() parameter 1 to be array, boolean given'. To fix it, before doing current() you should check if $reactions array is not empty
Laravel 5 - Clean code, where to keep business logic (controller example)
Below example of 'store' method of my controller Admin/MoviesController. It already seems quite big, and 'update' method will be even bigger. The algoritm is: Validate request data in CreateMovieRequest and create new movie with all fillable fields. Upload poster Fill and save all important, but not required fields (Meta title, Meta Description..) Then 4 blocks of code with parsing and attaching to movie of Genres, Actors, Directors, Countries. Request of IMDB's rating using third-party API My questions: Should I just move all this code to Model and divide it into smaller methods like: removeGenres($id), addGenres(Request $request), ... Are there some best practices? I'm talking not about MVC, but Laravel's features. At the moment to keep some logic behind the scene I'm using only Request for validation. public function store(CreateMovieRequest $request) { $movie = Movies::create($request->except('poster')); /* Uploading poster */ if ($request->hasFile('poster')) { $poster = \Image::make($request->file('poster')); $poster->fit(250, 360, function ($constraint) { $constraint->upsize(); }); $path = storage_path() . '/images/movies/'.$movie->id.'/'; if(! \File::exists($path)) { \File::makeDirectory($path); } $filename = time() . '.' . $request->file('poster')->getClientOriginalExtension(); $poster->save($path . $filename); $movie->poster = $filename; } /* If 'Meta Title' is empty, then fill it with the name of the movie */ if ( empty($movie->seo_title) ) { $movie->seo_title = $movie->title; } /* If 'Meta Description' is empty, then fill it with the description of the movie */ if ( empty($movie->seo_description) ) { $movie->seo_description = $movie->description; } // Apply all changes $movie->save(); /* Parsing comma separated string of genres * and attaching them to movie */ if (!empty($request->input('genres'))) { $genres = explode(',', $request->input('genres')); foreach($genres as $item) { $name = mb_strtolower(trim($item), 'UTF-8'); $genre = Genre::where('name', $name)->first(); /* If such genre doesn't exists in 'genres' table * then we create a new one */ if ( empty($genre) ) { $genre = new Genre(); $genre->fill(['name' => $name])->save(); } $movie->genres()->attach($genre->id); } } /* Parsing comma separated string of countries * and attaching them to movie */ if (!empty($request->input('countries'))) { $countries = explode(',', $request->input('countries')); foreach($countries as $item) { $name = mb_strtolower(trim($item), 'UTF-8'); $country = Country::where('name', $name)->first(); if ( empty($country) ) { $country = new Country(); $country->fill(['name' => $name])->save(); } $movie->countries()->attach($country->id); } } /* Parsing comma separated string of directors * and attaching them to movie */ if (!empty($request->input('directors'))) { $directors = explode(',', $request->input('directors')); foreach($directors as $item) { $name = mb_strtolower(trim($item), 'UTF-8'); // Actors and Directors stored in the same table 'actors' $director = Actor::where('fullname', trim($name))->first(); if ( empty($director) ) { $director = new Actor(); $director->fill(['fullname' => $name])->save(); } // Save this relation to 'movie_director' table $movie->directors()->attach($director->id); } } /* Parsing comma separated string of actors * and attaching them to movie */ if (!empty($request->input('actors'))) { $actors = explode(',', $request->input('actors')); foreach($actors as $item) { $name = mb_strtolower(trim($item), 'UTF-8'); $actor = Actor::where('fullname', $name)->first(); if ( empty($actor) ) { $actor = new Actor(); $actor->fill(['fullname' => $name])->save(); } // Save this relation to 'movie_actor' table $movie->actors()->attach($actor->id); } } // Updating IMDB and Kinopoisk ratings if (!empty($movie->kinopoisk_id)) { $content = Curl::get('http://rating.kinopoisk.ru/'.$movie->kinopoisk_id.'.xml'); $xml = new \SimpleXMLElement($content[0]->getContent()); $movie->rating_kinopoisk = (double) $xml->kp_rating; $movie->rating_imdb = (double) $xml->imdb_rating; $movie->num_votes_kinopoisk = (int) $xml->kp_rating['num_vote']; $movie->num_votes_imdb = (int) $xml->imdb_rating['num_vote']; $movie->save(); } return redirect('/admin/movies'); }
You need to think on how you could re-utilize the code if you need to use it in another classes or project modules. For starting, you could do something like this: Movie model, can improved in order to: Manage the way on how the attributes are setted Create nice functions in functions include/manage the data of relationships Take a look how the Movie implements the functions: class Movie{ public function __construct(){ //If 'Meta Title' is empty, then fill it with the name of the movie $this->seo_title = empty($movie->seo_title) ? $movie->title : $otherValue; //If 'Meta Description' is empty, //then fill it with the description of the movie $movie->seo_description = empty($movie->seo_description) ? $movie->description : $anotherValue; $this->updateKinopoisk(); } /* * Parsing comma separated string of countries and attaching them to movie */ public function attachCountries($countries){ foreach($countries as $item) { $name = mb_strtolower(trim($item), 'UTF-8'); $country = Country::where('name', $name)->first(); if ( empty($country) ) { $country = new Country(); $country->fill(['name' => $name])->save(); } $movie->countries()->attach($country->id); } } /* * Update Kinopoisk information */ public function updateKinopoisk(){} /* * Directors */ public function attachDirectors($directors){ ... } /* * Actores */ public function attachActors($actors){ ... } /* * Genders */ public function attachActors($actors){ ... } } Poster, you may considere using a service provider (I will show this example because I do not know your Poster model looks like): public class PosterManager{ public static function upload($file, $movie){ $poster = \Image::make($file); $poster->fit(250, 360, function ($constraint) { $constraint->upsize(); }); $path = config('app.images') . $movie->id.'/'; if(! \File::exists($path)) { \File::makeDirectory($path); } $filename = time() . '.' . $file->getClientOriginalExtension(); $poster->save($path . $filename); return $poster; } } Config file Try using config files to store relevant application constanst/data, for example, to store movie images path: 'images' => storage_path() . '/images/movies/'; Now, you are able to call $path = config('app.images'); globally. If you need to change the path only setting the config file is necessary. Controllers as injected class. Finally, the controller is used as a class where you only need to inject code: public function store(CreateMovieRequest $request) { $movie = Movies::create($request->except('poster')); /* Uploading poster */ if ($request->hasFile('poster')) { $file = $request->file('poster'); $poster = \PosterManager::upload($file, $movie); $movie->poster = $poster->filename; } if (!empty($request->input('genres'))) { $genres = explode(',', $request->input('genres')); $movie->attachGenders($genders); } // movie->attachDirectors(); // movie->attachCountries(); // Apply all changes $movie->save(); return redirect('/admin/movies'); }