How Can I filter by a function in silverstripe - silverstripe

I want to get a list of Files that are missing for a SilverStripe report.
The filesize is not directly stored in the database, instead I have to call method getSize()
If I try to iterate through the records and just choose the empty records, I only get the first level of the file hierarchy
$all_files = File::get()->sort('FileFilename');
$list = new ArrayList();
foreach ($all_files as $file) {
$file->mysize = $file->getSize();
if($file->getSize() == "" && $file->ClassName != 'SilverStripe\Assets\Folder'){
$list->push($file);
}
}
return $list;
How can I get all the records that are empty files?

there are 2 parts to your question:
to filter by a function, there is:
$list = $all_files->filterByCallback(function($file) {
return $file->getSize() == "" && $file->ClassName != 'SilverStripe\Assets\Folder';
});
This is a nice shorthand but does exactly what you are doing with your foreach
For the second part of your question:
I think you misunderstood how hierarchy in SilverStripe works.
If you do File::get() you will get all files regardless of their position in the hierarchy.
So you already have what you want I think. But if you only want files at the top level, you have to do $files = File::get()->filter(["ParentID" => 0]) and if you want a tree, then do a recursive loop with $file->Children().
Or you do what you've already done: fetch all of them, and then build a tree yourself based on the ParentID.

Related

Getting Taxonomy Terms for a Node ID in Drupal 8

I'm trying to add a preprocess hook to add classes based on taxonomy name to the body css of my drupal installation. I've managed to get all the information about the node based on doing some searching around and trial and error, but I'd like to take it to the next step and get all the taxonomy terms based on the particular node id.
My current preprocess code is follows:
function custom_preprocess_html(&$variables) {
// Add the node ID and node type to the body class
$body_classes = [];
$nodeFields =\Drupal::service('current_route_match')->getParameter('node')->toArray();
if (is_array($nodeFields) && count($nodeFields) > 0) {
if (isset($nodeFields['nid'])) {
$body_classes[] = 'node-' . $nodeFields['nid'][0]['value'];
}
if (isset($nodeFields['type'])) {
$body_classes[] = $nodeFields['type'][0]['target_id'];
}
}
$variables['attributes']['class'] = $body_classes;
}
It works fine and pulls down the information regarding the node. Based on the answer here it seems like all I should have to do is add the following line to get the taxonomy terms: $taxonomyTerms = $nodefields->get('field_yourfield')->referencedEntities(); but when I do so Drupal throws an error. I'll freely admit I'm new to Drupal 8, so any suggestions about where I'm going wrong (is field_yourfield not something that exists, maybe?) would be greatly appreciated.
If you are trying to get the referenced term names and add them as body classes, your approach seems a little bit off.
This is what I use:
function CUSTOM_preprocess_html(&$variables) {
// Entity reference field name.
$field_name = 'field_tags';
// Get the node object from the visited page.
// If the page is not a node detail page, it'll return NULL.
$node = \Drupal::request()->attributes->get('node');
// Let's make sure the node has the field.
if ($node && $node->hasField($field_name)) {
$referenced_entities = $node->get($field_name)->referencedEntities();
foreach ($referenced_entities as $term) {
$variables['attributes']['class'][] = \Drupal\Component\Utility\Html::cleanCssIdentifier($term->getName());
}
}
}

Best practice for storing simple variables like 'total rows' in Symfony2

In my symfony2 application I need to display some totals at the top of all pages, ie "Already 200,154,555 users registered".
I don't want to run the query to come up with that count on every page load. The solution I've come up with is to create a "variable" entity that would have two columns, name and value. Then I would set up a console command that runs on cron which would update these variable entities (eg "totalPeople") with a query that counted all the rows of people, etc.
This feels a little heavy handed... Is there a better solution to this problem?
You could set global parameters and add a service to rewrite them. Then call the service from your Command.
Or directly set up a service to read/write a file (as a json array for example).
Or set up a option table with a row storing the data. It's not going to be a resource intensive query that way.
Here is what I'm using to store RSS feeds (after I parsed them)
public function checkCache($data=array(), $path = '')
{
foreach ($data as $service => $feed)
{
$service = strtolower($service);
$service = str_replace(' ', '-', $service);
$path = $path.'web/bundles/citation/cache/rss/' . $service . '.cache';
if ((!file_exists($path) || time() - filemtime($path) > 900) && $cache = fopen($path, 'w'))
{
$rss_contents = $this->getFeed($feed); //fetch feed content & returns array
fwrite($cache, serialize($rss_contents));
fclose($cache);
return $rss_contents;
}
else
{
$cache = fopen($path, 'r');
return unserialize(file_get_contents($path));
fclose($cache);
}
}
}
You can implement that on your backend for example so every time an admin logs it'll check for cache and refresh only if it's too old. Although I'm quite fond of the 4AM cron job solution too.
You could use the pagination feature of doctrine (if you use doctrine). That will leverage the "limit" part of your queries (even with joins) and will give you a total count of rows (via a count query).

Modifying a field collection programmatically missing hostEntity fields

I am trying to modify a field collection in a node that already exists so I can change an image on the first element in an array of 3. The problem is, the hostEntity info is not set when I do a entity_load or entity_load_single so when I do a:
$field_collection_item->save(true); // with or without the true
// OR
$fc_wrapper->save(true); // with or without the true
I get the following error:
Exception: Unable to save a field collection item without a valid reference to a host entity. in FieldCollectionItemEntity->save()
When i print_r the field collection entity the hostEntity:protected fields are indeed empty. My field collection is setup as follows:
field_home_experts
Expert Image <--- Want to change this data only and keep the rest below
field_expert_image
Image
Expert Name
field_expert_name
Text
Expert Title
field_expert_title
Text
Here is the code I am trying to use to modify the existing nodes field collection:
$node = getNode(1352); // Get the node I want to modify
// There can be up to 3 experts, and I want to modify the image of the first expert
$updateItem = $node->field_home_experts[LANGUAGE_NONE][0];
if ($updateItem) { // Updating
// Grab the field collection that currently exists in the 0 spot
$fc_item = reset(entity_load('field_collection_item', array($updateItem)));
// Wrap the field collection entity in the field API wrapper
$fc_wrapper = entity_metadata_wrapper('field_collection_item', $fc_item);
// Set the new image in place of the current
$fc_wrapper->field_expert_image->set((array)file_load(4316));
// Save the field collection
$fc_wrapper->save(true);
// Save the node with the new field collection (not sure this is needed)
node_save($node);
}
Any help would be greatly appreciated, I am still quite new to Drupal as a whole (end-user or developer)
Alright so I think I have figured this out, I wrote up a function that will set a field collection values:
// $node: (obj) node object returned from node_load()
// $collection: (string) can be found in drupal admin interface:
// structure > field collections > field name
// $fields: (array) see usage below
// $index: (int) the index to the element you wish to edit
function updateFieldCollection($node, $collection, $fields = Array(), $index = 0) {
if ($node && $collection && !empty($fields)) {
// Get the field collection ID
$eid = $node->{$collection}[LANGUAGE_NONE][$index]['value'];
// Load the field collection with the ID from above
$entity = entity_load_single('field_collection_item', array($eid));
// Wrap the loaded field collection which makes setting/getting much easier
$node_wrapper = entity_metadata_wrapper('field_collection_item', $entity);
// Loop through our fields and set the values
foreach ($fields as $field => $data) {
$node_wrapper->{$field}->set($data);
}
// Once we have added all the values we wish to change then we need to
// save. This will modify the node and does not require node_save() so
// at this point be sure it is all correct as this will save directly
// to a published node
$node_wrapper->save(true);
}
}
USAGE:
// id of the node you wish to modify
$node = node_load(123);
// Call our function with the node to modify, the field collection machine name
// and an array setup as collection_field_name => value_you_want_to_set
// collection_field_name can be found in the admin interface:
// structure > field collections > manage fields
updateFieldCollection(
$node,
'field_home_experts',
array (
'field_expert_image' => (array)file_load(582), // Loads up an existing image
'field_expert_name' => 'Some Guy',
'field_expert_title' => 'Some Title',
)
);
Hope this helps someone else as I spent a whole day trying to get this to work (hopefully I won't be a noob forever in Drupal7). There may be an issue getting formatted text to set() properly but I am not sure what that is at this time, so just keep that in mind (if you have a field that has a format of filtered_html for example, not sure that will set correctly without doing something else).
Good luck!
Jake
I was still getting the error, mentioned in the question, after using the above function.
This is what worked for me:
function updateFieldCollection($node, $collection, $fields = Array(), $index = 0) {
$eid = $node->{$collection}[LANGUAGE_NONE][$index]['value'];
$fc_item = entity_load('field_collection_item', array($eid));
foreach ($fields as $field => $data) {
$fc_item[$eid]->{$field}[LANGUAGE_NONE][0]['value'] = $data;
}
$fc_item[$eid]->save(TRUE);
}
I hope this helps someone as it took me quite some time to get this working.

is it possible to set a field in drupal to some value by using nodeapi

So I have this field named field_movie_cast_count this is a field of a content named movie so what I want is that whenever the movie is updated (like I add another cast member) the field_movie_cast_count field would be updated too. I have successfully done it using this code:
function count_cast_nodeapi(&$node, $op) {
$id = $node->nid;
if($op == 'update' && $node->type == 'movie') {
$count=count($node->field_movie_cast);
$q = db_query("update content_type_movie set field_movie_cast_count_value = '$count' WHERE nid = '$id'");
}
}
Now, my boss told me that I should not use query. So, how do I achieve this by not using a database query?
is it not possible to just set field_movie_cast_count[]['value']=$count? i tried this code it doesnt work. lol
OR SHOULD I REPHRASE THE QUESTION is there any other way of displaying the count cast? aside from my way? cause maybe i did not understand my boss right.
hook_nodeapi() has a presave operation which is invoked just before the node (and field data) is committed to the database. The definition of the operation is:
The node passed validation and is about to be saved. Modules may use this to make changes to the node before it is saved to the database.
You can use it to update a field value, without resorting to hitting the database manually (which will be overwritten anyway) like so:
function MYMODULE_nodeapi(&$node, $op) {
if ($op == 'presave' && $node->type == 'movie') {
$node->field_movie_cast_count[0]['value'] = count($node->field_movie_cast);
}
}

How to use nodeapi in drupal?

guys i have this node type -> movie. this movie has casts in it. so now i was instructed to display the number of cast in a movie. how do i achieve this by using nodeapi? please someone guide mo through this. im very new to drupal coding here. here's what ive made so far:
function count_cast_nodeapi(&$node, $op) {
switch ($op) {
case 'update index':
if ($node->type == 'movie') {
$text = '';
$q = db_query('SELECT count(field_movie_cast_nid) FROM content_field_movie_cast ' .
'WHERE nid = %d', $node->nid);
if ($r = db_fetch_object($q)) {
$text = $r->count;
}
drupal_set_message($text);
}
}
}
but i have no idea where to put this code. where to save it. do i make a module of this? and is this code even correct?
also i have only copied this code. so i dont know what that 'update index' means. and who or what function will set that parameter $op
I can suggest you follow solution:
Create new integer CCK field for content type "movie" which will store the number of casts. Name it movie_cast_number.
This field will be updating in hook_nodeapi (similar to your example). For that you need to create custom module "count_cast". Place it to sites/all/modules directory.
In hook_nodeapi need to use $op "insert" and "update". $op generating by module node and means which operation is performed with node. Insert means new node has been created and update - existing node has been updated.
If you need custom output for this content type then you need to create node-movie.tpl.php in your theme directory.
Code which update the number of casts can looks follow way:
function count_cast_nodeapi(&$node, $op) {
switch ($op) {
case 'update':
case 'insert':
if ($node->type == 'movie') {
$result = db_result(db_query('
SELECT count(field_movie_cast_nid) cnt
FROM {content_field_movie_cast}
WHERE nid = %d
', $node->nid));
$node->field_movie_cast_number[0]['value'] = $result->cnt;
}
}
}

Resources