Silverstripe bulk save all dataobjects - silverstripe

I've added URLSegments to DataObjects (Products) so I can then display the ProductName as the URL....the code works fine:
public function onBeforeWrite(){
if($this->Name){
$this->URLSegment = SiteTree::GenerateURLSegment($this->Name);
if($object = DataObject::get_one($this->ClassName, "URLSegment='".$this->URLSegment."' AND ID !=".$this->ID)){
$this->URLSegment = $this->URLSegment.'-'.$this->ID;
}
} else {
$this->URLSegment = SiteTree::GenerateURLSegment($this->ClassName.'-'.$this->ID);
}
parent::onBeforeWrite();
}
But, I have over 1000 Products...is there any way of generating a bulk save for all Product Dataobjects in code (ie as a one-off) so I don't have to manually save each through the CMS??

Just create a controller with an index function and use more or less the same code.
<?php
class UpdateProducts extends Controller {
public function index() {
$products = DataObject::get('Products');
foreach ($products as $product) {
if (!$product->URLSegment) {
$product->write();
}
}
}
}
Then you can call the function once from the browser at http://example.com/UpdateProducts
This isn't super efficient so it really is only a one off. If the script times out, you can just run it again as the if statement in there means only products without a URLSegment will be updated.

I started to implement drzax solution, then found this, which implements it as a task. This task was created to add URLSegments to products, so fits my needs perfectly...
http://www.balbuss.com/creating-tasks/

Related

Change date format in xls export with Sonata Admin Bundle

I taken over responsibility for a Symfony2 application, built on the Sonata Admin Bundle, and have been asked to make a small change by the users. In the xls export of a list page, the dates all appear as e.g. Wed, 01 Aug 2012 00:00:00 +0200, but the Excel format is General. The users would like the data in this column to be an Excel date type, so that it is sort-able.
I have been able to find some information about export customization, but this mostly concerns choosing the list export file types, or which fields to include, rather than how to change the format in the exported document. A similar question was asked here (I think) but there is no answer.
I think this would (or should) be very simple, but it is certainly not obvious. Any help would be much appreciated.
A small improvement for Marciano's answer.
Makes the code a bit more resilient against sonata updates.
public function getDataSourceIterator()
{
$datasourceit = parent::getDataSourceIterator();
$datasourceit->setDateTimeFormat('d/m/Y'); //change this to suit your needs
return $datasourceit;
}
In my admin class EmployeeAdmin I use getExportFields function specifies which fields we want to export:
public function getExportFields() {
return array(
$this->trans('list.label_interview_date') => 'interviewDateFormatted'
);
}
interviewDateFormatted is actually a call to the corresponding entity (Employee) method getInterviewDateFormatted which looks like this:
public function getInterviewDateFormatted() {
return ($this->interviewDate instanceof \DateTime) ? $this->interviewDate->format("Y-m-d") : "";
}
This way I can change date format or do other necessary changes to the fields I want to export.
this is my code. It's work!
use Exporter\Source\DoctrineORMQuerySourceIterator;
use Sonata\DoctrineORMAdminBundle\Datagrid\ProxyQuery;
use Sonata\AdminBundle\Datagrid\ProxyQueryInterface;
and function:
/**
* {#inheritdoc}
*/
public function getDataSourceIterator()
{
$datagrid = $this->getDatagrid();
$datagrid->buildPager();
$fields=$this->getExportFields();
$query = $datagrid->getQuery();
$query->select('DISTINCT ' . $query->getRootAlias());
$query->setFirstResult(null);
$query->setMaxResults(null);
if ($query instanceof ProxyQueryInterface) {
$query->addOrderBy($query->getSortBy(), $query->getSortOrder());
$query = $query->getQuery();
}
return new DoctrineORMQuerySourceIterator($query, $fields,'d.m.Y');
}
just add this in your admin (overriding a method of the admin class you are extending). Found it reading the code. It's not in the docs.
public function getDataSourceIterator()
{
$datagrid = $this->getDatagrid();
$datagrid->buildPager();
$datasourceit = $this->getModelManager()->getDataSourceIterator($datagrid, $this->getExportFields());
$datasourceit->setDateTimeFormat('d/m/Y'); //change this to suit your needs
return $datasourceit;
}
Did you managed to make it work?
Date format is defined as parameter for new DoctrineORMQuerySourceIterator.php (https://github.com/sonata-project/exporter/blob/master/lib/Exporter/Source/DoctrineORMQuerySourceIterator.php)
DoctrineORMQuerySourceIterator.php is created inside getDataSourceIterator function (https://github.com/sonata-project/SonataDoctrineORMAdminBundle/blob/2705f193d6a441b9140fef0996ca392887130ec0/Model/ModelManager.php)
Inside of Admin.php there is function calling it:
public function getDataSourceIterator()
{
$datagrid = $this->getDatagrid();
$datagrid->buildPager();
return $this->getModelManager()->getDataSourceIterator($datagrid, $this->getExportFields());
}
If you write your own getDataSourceIterator() then you can change date format.
Since sonata-admin 4.0, the function getDataSourceIterator() is tagged as final, so you can't override it.
So you need to create a decorating iterator :
<?php
namespace App\Service\Admin;
use Sonata\AdminBundle\Datagrid\ProxyQueryInterface;
use Sonata\AdminBundle\Exporter\DataSourceInterface;
use Sonata\DoctrineORMAdminBundle\Exporter\DataSource;
use Sonata\Exporter\Source\DoctrineORMQuerySourceIterator;
use Sonata\Exporter\Source\SourceIteratorInterface;
class DecoratingDataSource implements DataSourceInterface
{
private DataSource $dataSource;
public function __construct(DataSource $dataSource)
{
$this->dataSource = $dataSource;
}
public function createIterator(ProxyQueryInterface $query, array $fields): SourceIteratorInterface
{
/** #var DoctrineORMQuerySourceIterator $iterator */
$iterator = $this->dataSource->createIterator($query, $fields);
$iterator->setDateTimeFormat('Y-m-d H:i:s');
return $iterator;
}
}
And add it in your config/services.yaml
services:
...
App\Service\Admin\DecoratingDataSource:
decorates: 'sonata.admin.data_source.orm'
arguments: ['#App\Services\Admin\DecoratingDataSource.inner']
Found here : https://docs.sonata-project.org/projects/SonataDoctrineORMAdminBundle/en/4.x/reference/data_source/

Laravel Eloquent - Encrypting/Decrypt Data on call

I can use Crypt to encrypt/decrypt my data. I want to encrypt some information in my db (such as the name, email, phone number to name a few).
Assuming that I want EVERYTHING to be encrypted, I want to be able to do this in the background by itself, which I can perform by overwriting the create and save functions:
// For instance, the save() function could become
public function save(array $options = array())
{
foreach ($this->attributes as $key => $value)
{
if (isset($value)) $this->attributes[$key] = Crypt::encrypt($value);
}
return parent::save($options);
}
Now, I want the decryption to be performed the same way, so that when I say User::find($id), the returned $user is already decrypted. Also other functions such as firstOrFail() get() first() and all to work as well.
I also would like this functionality to be extended when I use relationships (so User::with('someOtherTable')->find($id) also work).
Would this be possible? If this is not possible, I am thinking of creating a helper function decyrpt()
function decrypt($array)
{
if (!is_array($array)) return Crypt::decrypt($array);
$result = [];
foreach($array as $key => $value) $result[$key] = decrypt($value);
return $result;
}
And pass all my results through this first, and then start using them, but it would be nicer if Laravel would provide this, or if there was a "Laravel Way" of doing this.
It doesn't really make sense to encrypt everything. For example, you never want to encrypt the primary key; that doesn't even make sense. Likewise you probably don't want to encrypt the date fields; you'll lose the ability to perform any sort of SQL query on them.
With that in mind, you can try something like this:
class BaseModel extends Eloquent {
protected $encrypt = [];
public function setAttribute($key, $value)
{
if (in_array($key, $this->encrypt))
{
$value = Crypt::encrypt($value);
}
return parent::setAttribute($key, $value);
}
public function getAttribute($key)
{
if (in_array($key, $this->encrypt))
{
return Crypt::decrypt($this->attributes[$key]);
}
return parent::getAttribute($key);
}
public function attributesToArray()
{
$attributes = parent::attributesToArray();
foreach ($attributes as $key => $value)
{
if (in_array($key, $this->encrypt))
{
$attributes[$key] = Crypt::decrypt($value);
}
}
return $attributes;
}
}
then have all you models extend this one, and set the $encrypt property to whatever columns you want encrypted for that particular model.
P.S. If you want to use Eloquent's accessor functionality, you'll have to play with this a bit more.
It's worth mentioning Elocrypt library for Laravel 4. It's a more elaborate solution that works the same way. If you're using Laravel 5 use this one instead: Elocrypt 5.

Is there a way to access the symfony2 container within an SQLFilter?

is there any possibility to get the service-container of symfony2 within an SQLFilter or can i maybe directly use a service as SQLFilter?
I know that this isn't a "clean" way, but i have to perform several checks directly before the final submit of the query gets fired (as i have to append conditions to the WHERE-statement, i can't use lifecycle-events at this point).
it's not clean but you could try this:
<?php
class MyBundle extends Bundle
{
public function boot()
{
$em = $this->container->get('doctrine.orm.default_entity_manager');
$conf = $em->getConfiguration();
$conf->addFilter(
'test',
'Doctrine\Filter\TestFilter'
);
$em->getFilters()->enable('test')->setContainer($this->container);
}
}

Symfony Write to sfGuard Table

I know it's not ideal, but I have a few extra fields on the sfGuard User table and I would like to write to it from another module. There is a specific field that is a simple integer but I would like it to -1 each time they perform a specific task. Here is what I tried. I don't get an error message but it also doesn't write the number to the table.
public function executePublish(sfWebRequest $request)
{
$this->user = $this->getUser()->getGuardUser();
$times = ($this->user->getTimes() - 1);
$this->getUser()->getGuardUser()->setTimes($times);
}
This is the "Publish" action for a different module. Am I doing this wrong? Thanks.
You didn't maked an INSERT to the database by setTimes($times)
you have to call save() method after setting all values of object.
It should look like this:
public function executePublish(sfWebRequest $request)
{
$this->user = $this->getUser()->getGuardUser();
$times = ($this->user->getTimes() - 1);
$this->user->setTimes($times);
$this->user->save();
}

Virtual fields in Symfony2

I am still thinking about the best way to work with tags in Symfony. I did look at FPNTagBundle, but I didn't find an easy way to work this into the CRUD forms.
I also found http://xoxco.com/clickable/jquery-tags-input which would give a perfect widget. As it in- and outputs comma separated strings, I thought I could just define a virtual field in my model, that displays the tag object array as such a list.
public function addTag(\Wein\StoreBundle\Entity\Tag $tag)
{
$this->tag[] = $tag;
$this->makeTagFieldFromTags();
}
public function setTagField($tagField)
{
$this->tagField = $tagField;
$this->makeTagsFromTagField();
}
public function makeTagsFromTagField()
{
$tags=explode(',', $this->tagField);
$tagObjects=array();
$em = $this->getDoctrine()->getEntityManager();
foreach($tags as $tag) {
$tag=trim($tag);
$tagObject = **???**;
$tagObjects[]=$tagObject;
}
$this->tag=$tagObjects;
}
public function makeTagFieldFromTags()
{
$tags=array();
foreach($this->tag as $tag) {
$tags[]=$tag->__toString();
}
$this->tagField = implode(',', $tags);
}
The I could just use a form element on this field. Unfortunatly, I don't see a way to translate the strings into Tag-objects insite the entity, as I don't have access to the entity manager.
So what is the clean way?
The clean way is to use a data transformer. It transforms the strings into your tag entities at the "form" side and not in the entity, so you can keep your entity clean.

Resources