Access value from object(with private property) in array using php - phpexcel

I want to get value from object in array. Object property has been set to private. So, I could not access the value.
I try to convert private to public using Php ReflectionClass.
VarDump of Object In Array($obj_array)
array(1)
{
[23]=>
object(PhpOffice\PhpSpreadsheet\Worksheet\RowDimension)#6167 (7)
{
["rowIndex":"PhpOffice\PhpSpreadsheet\Worksheet\RowDimension":private]=>
int(23)
["height":"PhpOffice\PhpSpreadsheet\Worksheet\RowDimension":private]=>
string(3) "7.5"
["zeroHeight":"PhpOffice\PhpSpreadsheet\Worksheet\RowDimension":private]=>
bool(false)
["visible":"PhpOffice\PhpSpreadsheet\Worksheet\Dimension":private]=>
bool(true)
["outlineLevel":"PhpOffice\PhpSpreadsheet\Worksheet\Dimension":private]=>
int(0)
["collapsed":"PhpOffice\PhpSpreadsheet\Worksheet\Dimension":private]=>
bool(false)
["xfIndex":"PhpOffice\PhpSpreadsheet\Worksheet\Dimension":private]=>
NULL
}
}
Code to convert private object to public
foreach($obj_array as $key=>$value)
{
$r = new ReflectionObject($value);
$p = $r->getProperty('height');
$p->setAccessible(true);
echo $obj->height.'<br/>';
}
I expect to get height value,7.5 from the object. It end up with this error.
Uncaught Error: Cannot access private property
PhpOffice\PhpSpreadsheet\Worksheet\RowDimension::$height
Thanks in advance.

Just use the getRowHeight function it returns the private height property internally. (as seen in the source of PhpSpreadSheet)
/**
* Get Row Height.
*
* #return float
*/
public function getRowHeight()
{
return $this->height;
}

Related

Doctrine filter not working on itemOperations (Api-Platform)

API Platform version(s) affected: 2.6.8
Description
In a project which uses PostgreSQL and API-Platform, I need to filter all records by a locale string. A doctrine filter is my preferred choice to do so.
This is the filter:
class LocaleFilter extends SQLFilter
{
public const LOCALE_FILTER_NAME = 'locale_filter';
public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias): string
{
if (!$targetEntity->reflClass->implementsInterface(LocalizedEntityInterface::class)) {
return '';
}
return $targetTableAlias . '.locale = ' . $this->getParameter('locale');
}
}
The parameter locale will be set on each onKernelRequest event, the locale is the value of the header X-Locale:
public function onKernelRequest(RequestEvent $event): void
{
$locale = $event->getRequest()->headers->get('X-Locale');
$this->setFilterLocale($locale);
}
private function setFilterLocale(string $locale): void
{
if (!$this->entityManager->hasFilters()) {
return;
}
$localeFilter = $this->entityManager->getFilters()->getFilter(LocaleFilter::LOCALE_FILTER_NAME);
$localeFilter->setParameter('locale', $locale);
}
Now, when I send a request to a collectionOperations endpoint, such as http://example.com/products with the X-Locale header value de_DE, the filter is working and I get a response which contains only the according data in de_DE. When I send a request with locale fr_FR, I get a response with data in fr_FR.
But, when I send a request with the same X-Locale header to a itemOperations endpoint like http://example.com/products/<a-existing-id> I'm getting the error message The parameter "locale" is not set which comes from doctrine.
After investigating that issue, I can say that it works when I override the default ItemDataProvider from API-platform:
<?php
namespace App\DataProvider;
[...]
class ItemDataProvider implements ItemDataProviderInterface
{
public function __construct(
private readonly EntityManagerInterface $entityManager,
private readonly RequestStack $requestStack,
) {
}
public function getItem(string $resourceClass, $id, ?string $operationName = null, array $context = []): object
{
$locale = $this->requestStack->getMainRequest()->headers->get('X-Locale');
if ($this->entityManager->hasFilters()) {
$localeFilter = $this->entityManager->getFilters()->getFilter(LocaleFilter::LOCALE_FILTER_NAME);
$localeFilter->setParameter('locale', $locale);
}
$query = $this->entityManager->getRepository($resourceClass)
->createQueryBuilder('x')
->where('x.publicId = :pubid')
->setParameter('pubid', $id)
->getQuery();
return $query->getOneOrNullResult();
}
}
But is still required to set the filter value again in "my own" ItemDataProvider. If I delete the first 7 lines of the method getItem of the ItemDataProvider, I get the same error from above.
That doesn't make sense like that, does it? It seems like Api-Platform overrides the Doctrine filters in the default ItemDataProvider and make them useless. Howewer, I didn't found the reason for that issue.
Overriding the ItemDataProvider is a working workaround, but I don't think it's a good one, since the cause is more likely a bug and that way some features of Api-Platform are no longer present in the whole project.

The Doctrine make a not correct diff for an entity with ENUM type

I did a special ENUM type for property type of entity Command by cookbook.
The property looks like this one:
/**
* #var CommandType
*
* #ORM\Column(type="command_type")
*/
protected $type;
This snippet describe new type for doctrine:
final class CommandTypeType extends EnumerableType
{
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
{
return 'VARCHAR(30)';
}
public function getName(): string
{
return 'command_type';
}
protected function getClassName(): string
{
return CommandType::class;
}
}
After first run command /bin/console doctrine:migrations:diff for an entity, I get the migration and it looks correct
final class Version2020 072720500 extends AbstractMigration
{
public function up(Schema $schema) : void
{
$this->addSql("CREATE TABLE commands (
id INT AUTO_INCREMENT NOT NULL,
name VARCHAR(30) DEFAULT NULL,
command VARCHAR(100) DEFAULT NULL,
type varchar(30) NOT NULL,
PRIMARY KEY(id)
) DEFAULT CHARACTER SET UTF8 COLLATE `UTF8_unicode_ci` ENGINE = InnoDB");
}
public function down(Schema $schema) : void
{
$this->addSql('DROP TABLE commands');
}
}
Next step is run command /bin/console doctrine:migrations:migrate and it works correct, table created.
Then I run command /bin/console doctrine:migrations:diff one more time and get new migration
final class Version20200727205035 extends AbstractMigration
{
public function up(Schema $schema) : void
{
$this->addSql('ALTER TABLE commands CHANGE type type VARCHAR(30) NOT NULL');
}
public function down(Schema $schema) : void
{
$this->addSql('ALTER TABLE commands CHANGE type type VARCHAR(30) CHARACTER SET utf8 NOT NULL COLLATE `utf8_unicode_ci`');
}
}
I don't know why the second diff generated this strange migration.
What I did wrong?
Symfony 4.4
doctrine/doctrine-migrations-bundle 3.0.1
doctrine/migrations 3.0.1
Doctrine needs a comment to be added to the column to detect that the custom type has been applied to the column.
Check the requiresSQLCommentHint method is implemented and returns true or add it to your custom type.
final class CommandTypeType extends EnumerableType
{
...
public function requiresSQLCommentHint(AbstractPlatform $platform): bool
{
return true;
}
}
You should see a comment to be added to your column on the next migration and nothing more.

PostWrite event using services according to an event's property

I have an EventSubscriberInterface called PostCreation listening to the POST_WRITE event.
The method called when the event occurs must call a service according to the "type" attribute of the object which the event occured on.
The following code is a working example of what I want:
<?php
class PostCreation implements EventSubscriberInterface
{
private $processAfterPostCreationTypeAService;
private $processAfterPostCreationTypeBService;
public function __construct(
ProcessAfterPostCreationTypeAService $processAfterPostCreationTypeAService,
ProcessAfterPostCreationTypeBService $processAfterPostCreationTypeBService
) {
$this->processAfterPostCreationTypeAService = $processAfterPostCreationTypeAService;
$this->processAfterPostCreationTypeBService = $processAfterPostCreationTypeBService;
}
public static function getSubscribedEvents(): array
{
return [KernelEvents::VIEW => ['processPostCreation', EventPriorities::POST_WRITE]];
}
public function processPostCreation(GetResponseForControllerResultEvent $event): void
{
$myObject = $event->getControllerResult();
if (!$event->getRequest()->isMethod('POST') ||
!$myObject instanceof MyClass
) {
return;
}
/* some code */
if($myObject->getType() === 'A') {
$this->processAfterPostCreationTypeAService->doSomething($myObject);
} else if($myObject->getType() === 'B') {
$this->processAfterPostCreationTypeBService->doSomething($myObject);
}
/* some code */
}
}
Doing it that way is not maintainable so I would like to find another solution, but I can't find it myself so I need help:
The constructor of PostCreation can't know the object.type because it's called before the event occurs
I can't instantiate easily ProcessAfterPostCreationTypeAService or ProcessAfterPostCreationTypeBService in processPostCreation method because they are services as well (and needs dependancy injection, configured to be autowired)
I'm sure dependancy injection could help me, but I can't find how.
So how to do something maintainable?

JavaFX TableView with simple xml model

For configuration I use simple xml. I also use this model for TableView. My problem is using of boolean. TableView needs BooleanProperty but simple xml cannot access to this object, obviously. How can I combine this without write big code?
Model
#Root(name="scriptdata")
#Order(elements={"title", "active"})
public class ScriptData {
#Element (required=true)
private String title;
#Element (required=false)
private BooleanProperty active;
/**
*
* #param title
* #param active
*/
public ScriptData() {
this.active = new SimpleBooleanProperty(active);
}
public boolean isActive() {
return active.getValue();
}
public void setActive(boolean active) {
this.active.set(active);
}
CellFactory
modulActiveColumn.setCellValueFactory(new PropertyValueFactory<>("active"));
modulActiveColumn.setCellFactory(CheckBoxTableCell.forTableColumn(modulActiveColumn));
modulActiveColumn.setOnEditCommit((EventHandler<CellEditEvent>) t -> {
((ScriptData) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setActive((boolean) t.getNewValue());
}
My problem is using of boolean. TableView needs BooleanProperty
You're wrong. In fact the TableView never gains access to the BooleanProperty object stored in the active field of it's items.
PropertyValueFactory uses reflection to
Access a property object by invoking a method with the constructor parameter concatenated with "Property". (This method would be called activeProperty() in your case).
If the above doesn't work it wraps the value returned by a the getter for the property in a ObservableValue. (The name of the getter in this case is getActive() or isActive).
In your case the cellValueFactory does something similar to the following factory
modulActiveColumn.setCellValueFactory(cellData -> new SimpleBooleanProperty(cellData.getValue().isActive()));
Using a boolean field to store the data achieves exactly the same result in your case. The drawback of this approach is that programatic updates of the property do not trigger an update of the TableView and the edits need to be handled manually.
#Root(name="scriptdata")
#Order(elements={"title", "active"})
public class ScriptData {
#Element (required=true)
private String title;
#Element (required=false)
private boolean active;
/**
*
* #param title
* #param active
*/
public ScriptData() {
}
public boolean isActive() {
return active;
}
public void setActive(boolean active) {
this.active = active;
}
}

What is the use of return this["key"]?

This is an simple Scenario , i totally understand the first code of set & get.
private string exampleValue;
public string Example
{
get { return this.exampleValue ; }
set { this.exampleValue = value ; }
}
How is it differ from this code :
public string Example
{
get
{
return this["Example"].ToString();
}
}
It is an Indexer property.
You can define your own indexers in a class. For example, here is a string indexer.
class myClass
{
...
public object this[string name]
{
get
{
... implement code here to retrieve the object that correspond to your string index
}
set
{
... implement code here to store the object that correspond to your string index
}
}
}
Indexers are not limited to string or integer. For example, the Dictionary object uses a generic Indexer:
public class Dictionary<TKey, TValue> : IDictionary<TKey, TValue>, ...
...
public TValue this[TKey key] { get; set; }
Also, the Session and Application objects in ASP.NET uses a string indexer.

Resources