Jump to content


  • Posts

  • Joined

  • Last visited

  • Days Won


NotionCommotion last won the day on September 22 2021

NotionCommotion had the most liked content!

Contact Methods

  • Website URL

Profile Information

  • Gender
    Not Telling

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

NotionCommotion's Achievements

Prolific Member

Prolific Member (5/5)




Community Answers

  1. Yeah, the Permission vs PermissionEnum thing kind of confusing to me as well! Yes, Doctrine has generic support for them. Will downgrade and wait it out. Thanks PS. I had tried to throw an __isString() in my enum, but evidently doing so is a no-no. Guess an __isInt() is a bit too niche and I understand why it doesn't exist.
  2. Thanks kicken, I had attempted to change App\Doctrine\Types\PermissionType (this is what you meant by doctrine configuration, right?) to not use a generic small int type and instead use a normal int type but it remained small int. Thinking it is a doctrine bug https://github.com/doctrine/orm/issues/10066.
  3. The following Doctrine method throws error Warning: Object of class App\\Entity\AccessControl\Acl\PermissionEnum could not be converted to int namespace Doctrine\DBAL\Types; class SmallIntType extends Type implements PhpIntegerMappingType { ... public function convertToPHPValue($value, AbstractPlatform $platform) { return $value === null ? null : (int) $value; } ... } PermissionEnum is a backed integer enum. enum PermissionEnum: int { case public = 0b00; case owner = 0b01; case restrict = 0b10; case extra = 0b11; } Similar to the __toString() magic method, is there a way to change my PermissionEnum so when type casted as an integer, returns an integer? Supplementary info below if of interest While outside of the scope of this question, the following added lines of code were added by Doctrine's latest commit and if removed, the issue goes away. PermissionEnum is used by class Permission which in turn is converted to an integer when saving the the database. namespace Doctrine\ORM\Query; class SqlWalker implements TreeWalker { public function walkSelectExpression($selectExpression) { ... if (! empty($mapping['enumType'])) { $this->rsm->addEnumResult($columnAlias, $mapping['enumType']); } ... } } namespace App\Doctrine\Types; use App\Entity\AccessControl\Acl\Permission; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Types\ConversionException; use Doctrine\DBAL\Types\Type; final class PermissionType extends Type { private const PERMISSION = 'permission'; public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform): string { return $platform->getSmallIntTypeDeclarationSQL($fieldDeclaration); } public function convertToPHPValue($value, AbstractPlatform $platform): mixed { if (null === $value) { return null; } if (\is_int($value)) { return Permission::createFromValue($value); } throw ConversionException::conversionFailedInvalidType($value, $this->getName(), ['int', 'null']); } public function convertToDatabaseValue($value, AbstractPlatform $platform): mixed { if (null === $value) { return null; } if ($value instanceof Permission) { return $value->getValue(); } throw ConversionException::conversionFailedInvalidType($value, $this->getName(), [Permission::class, 'null']); } public function requiresSQLCommentHint(AbstractPlatform $platform): bool { return true; } public function getName(): string { return self::PERMISSION; } } class Permission { private const READ = 0b0000000011; private const CREATE = 0b0000001100; private const MODIFY = 0b0000110000; private const DELETE = 0b0011000000; private const COWORKER = 0b0100000000; // Not currently used. private const OWNER = 0b1000000000; // Not currently used. public function __construct( private ?PermissionEnum $read=null, private ?PermissionEnum $create=null, private ?PermissionEnum $modify=null, private ?PermissionEnum $delete=null, private ?bool $restrictToOwner=null, private ?bool $coworkers=null ) { } public function getValue(): ?int { if($this->hasNullValue()) { throw new Exception('Permission has NULL values.'); } $rs = $this->read->value | $this->create->value << 2 | $this->modify->value << 4 | $this->delete->value << 6; $rs = $this->coworkers ? $rs | self::COWORKER : $rs & ~self::COWORKER; return $this->restrictToOwner ? $rs | self::OWNER : $rs & ~self::OWNER; } }
  4. Sorry, thought I had included links to both the github and composer pages but evidently not. Github: https://github.com/api-platform/core/blob/3.0/src/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php Packagist: https://packagist.org/packages/api-platform/core#3.0.x-dev
  5. I wish to use this version of ApiPlatformExtension which is the most current on the 3.0 branch. Note that there is no tag associated with it as v3.0.0 is several days older. Now, on packagist, there are several versions available, and based on the 2022-09-20 08:49 UTC date associated with 3.0.x-dev, it appears that is the one I want. It is my understanding that this branch only looks like a version, but isn't as indicated by the .x in the name. How should I make my project use the specific file which I stated above I wish to use? The project is not in production, so while maybe not ideal, I am okay with using an untagged version. I've tried the following and multiple similar attempts, but only get the previous version of the file. { "type": "project", "license": "proprietary", "minimum-stability": "stable", "prefer-stable": true, "require": { "php": ">=8.1", "api-platform/core": "3.0.x-dev", ... } }
  6. Agreed it didn't make sense. Just didn't know whether I was missing something else. Thanks
  7. Per the docs: Okay, guess that makes sense. But what doesn't make sense (at least to me) is why I am getting the following error. Can anyone explain why I am getting it? final class DocumentExtension implements QueryCollectionExtensionInterface { final private const EXEMPT_USER_ROLE = 'ROLE_TENANT_ADMIN'; private function addWhere(QueryBuilder $queryBuilder, string $resourceClass): bool { if ($this->security->isGranted(self::EXEMPT_USER_ROLE)) { return false; } } }
  8. Agree it will likely not be an issue and even more agree it will not be an issue if only occurring when the database is being changed. Figured so much. Initially thought that if the content that was being saved was identical both times, it would somehow go through, but more I think about it, the more I think unlikely. I've conceded on not trying to overly optimize, however, I think this is a great solution. Let me check right now whether it works. On face value, it works great. The only thing I am not sure about is whether some method such as getId() is being invoked on it and it is being populated from the database. I looked at the SQL log and found that the user table is being queried three times. Guess if I was really concerned about performance, I wouldn't be using Doctrine. Appreciate the help!
  9. Thanks kicken! Was hoping it was going to be that easy, but nothing is ever easy! I have a TokenUser object which is my limited user object which is stored in the token. When passing it to BlameableListener, I get error: The class 'App\Security\TokenUser' was not found in the chain configured namespaces App\Entity, Tbbc\MoneyBundle\Entity, Money TokenUser contains a Ulid property, and when passing it to BlameableListener, I get error: The class 'Symfony\Component\Uid\Ulid' was not found in the chain configured namespaces App\Entity, Tbbc\MoneyBundle\Entity, Money So, then I tried passing BlameableListener the Ulid string value, but get error: Blame is reference, user must be an object I apply Blamable as an association, so I expect that explains the last error. #[ORM\ManyToOne(targetEntity: UserInterface::class)] #[ORM\JoinColumn(nullable: false)] #[Gedmo\Blameable(on: 'update')] protected ?UserInterface $updateBy = null; I don't really understand the chain configured namespaces errors. My condensed security.yaml is: security: providers: app_logon_user_provider: id: App\Security\LogonUserProvider jwt: lexik_jwt: class: App\Security\TokenUser firewalls: main: provider: jwt jwt: ~ json_login: provider: app_logon_user_provider check_path: /authentication_token username_path: email password_path: password success_handler: lexik_jwt_authentication.handler.authentication_success failure_handler: lexik_jwt_authentication.handler.authentication_failure TokenUser is: final class TokenUser implements JWTUserInterface, BasicUserInterface { public function __construct(private Ulid $id, private OrganizationType $type, private array $roles, private Ulid $organizationId, private ?Ulid $tenantId) { } public static function createFromPayload($id, array $payload):self { return new self(Ulid::fromString($id), OrganizationType::fromName($payload['type']), $payload['roles'], Ulid::fromString($payload['organizationId']), $payload['tenantId']?Ulid::fromString($payload['tenantId']):null); } // Some more methods... } My LogonUserProvider::loadUserByIdentifier() queries the DB for the user and if it exists, returns the doctrine User. I also tried returning TokenUser instead of the Doctrine User, but I recall it also having issues. Maybe I should be? My JWTCreatedListener sets the payload based on the Doctrine User, and the Jwt package takes it from there. final class JWTCreatedListener { public function onJWTCreated(JWTCreatedEvent $event) { $user = $event->getUser(); $payload = $event->getData(); $payload['organizationId'] = $user->getOrganization()->toRfc4122(); $payload['tenantId'] = ($tenant=$user->getTenant())?$tenant->toRfc4122():null; $event->setData($payload); } } I also have another AuthenticationSuccessListener which sets some public data (never understood why this needs to be a separate listener, but oh well...). final class AuthenticationSuccessListener { public function onAuthenticationSuccessResponse(AuthenticationSuccessEvent $authenticationSuccessEvent): void { $user = $authenticationSuccessEvent->getUser(); if (!$user instanceof UserInterface) { return; } $data = $authenticationSuccessEvent->getData(); $data['data'] = [ 'id' => $user->getId()->toRfc4122(), 'firstName' => $user->getFirstName(), 'lastName' => $user->getLastName(), 'roles' => $user->getRoles(), ]; $authenticationSuccessEvent->setData($data); } } The only way I have been able to eliminate errors is pass BlameableListener the hydrated Doctrine Users. Instead of getting this Doctrine User on any POST/PUT/PATCH requests as I suggested I might do in my initial post, I am doing so during the preFlush event which I suppose is a little better. Still kind of annoys me, and would rather not do so. Any advise? Thanks!
  10. I am using a 3rd party plugin called blamable which sets the createBy and updateBy property when entity which have these properties are changed. While it happens to by Symfony/Doctrine related, I don't think the solution need be based on either. It is the responsibility of the developer to set the user within the blamable listener and I have seen this be done by setting it upon each request regardless of whether it will be required for the given request. I happen to be using JWT's where a minimal user entity is stored in the token and used for most requests and I just query the DB and transform to the real user entity where needed. As such, I don't want to make the DB call for all requests, and instead also listen for entities which contain these properties are created or updated, but listen faster than the blamable listener and set it first. All great... Until I discover the blamable bundle also considers an entity changed when something is added or removed from a many-to-many relationship to an entity, and my listener doesn't get triggered, and updateBy is set to null resulting in a not-null constraint error. I suppose I "could" try to predict these edge cases, but feel this is getting too fragile. One option is to set it very early for all post/patch/put requests before I know whether it will actually be necessary, but my OCD nature doesn't want to (actually, as I write this, thinking doing so might not be so bad). Instead, I would like to set it very early with some "preliminary user object" which gets converted to the real user object only when the blamable plugin actually tries to do something with this preliminary user object. Below is a very condensed version of BlameableListener and AbstractTrackingListener. I am questioning whether I am going down a rabbit hole which doesn't come back up. Think so? If it might be feasible, ideas where to start? Was thinking my preliminary user object might use overloading so that I know when any of the methods are called, but I don't think doing so will be allowed by an interface. Maybe I am going about this totally the wrong way. class BlameableListener extends AbstractTrackingListener { protected $user; // This is the method I am suppose to set before blamable needs it. public function setUserValue($user) { $this->user = $user; } public function getFieldValue($meta, $field, $eventAdapter) { // Does a little work and returns $this->user. } public function prePersist(EventArgs $args) { // Checks things out and maybe calls updateField(). This is easy as I can independent listen when an entity is persisted (Doctrine talk as scheduled to be intered into the DB). } public function onFlush(EventArgs $args) { // Checks things out and maybe calls updateField(). This one is not easy as flush happens after the persist event and I have missed the chance to set the user. } protected function updateField($object, $eventAdapter, $meta, $field) { // $object is an entity which has a updateBy property which needs to be set with the logged on user $property = $meta->getReflectionProperty($field); // $property is ReflectionProperty Object ([name] => updateBy [class] => App\Entity\Asset\Asset) ... $newValue = $this->getFieldValue($meta, $field, $eventAdapter); // $newValue should be the user but will be NULL if I haven't set it. ... $property->setValue($object, $newValue); } }
  11. Is there any slick way of applying a method conditionally like my made up syntax below? Thanks $flag = true; $obj ->setName('bob') ->setAge(99) ->?$flag->setFoo('xxx'); //Only call setFoo if $flag is true. $obj ->setName('bob') ->setAge(99) ->?$flag->setFoo('xxx'):setBar('yyy'); //Call setFoo if $flag is true, else call setBar public function setName(string $name):self;
  12. The following four methods were changed. The first three seem to be correct but not sure about the last one (hasPhysicalMedia). Agree? Best way to fix is just type casting the returned value? Thanks public function getPhysicalMedia(): ?PhysicalMedia { - return $this->activeMedia?$this->activeMedia->getPhysicalMedia():null; + return $this->activeMedia?->getPhysicalMedia(); } public function getMediaType(): ?MediaType { - return $this->activeMedia?$this->activeMedia->getMediaType():null; + return $this->activeMedia?->getMediaType(); } public function getFilename(): ?string { - return $this->activeMedia?$this->activeMedia->getFilename():null; + return $this->activeMedia?->getFilename(); } public function hasPhysicalMedia(): bool { - return $this->activeMedia && $this->activeMedia->hasPhysicalMedia(); + return $this->activeMedia?->hasPhysicalMedia(); } class ActiveMedia { public function getPhysicalMedia(): PhysicalMedia { return $this->physicalMedia; } public function getMediaType(): ?MediaType { return $this->mediaType; } public function getFilename(): ?string { return $this->filename; } public function hasPhysicalMedia(): bool { return (bool) $this->physicalMedia; } }
  13. Thanks requinix, Seem like if a user doesn't have a home directory, php assumes it is at the typical /home/abtfile. Ended up going with "abtfile:x:1001:1001::/var/www/abtfile:/usr/sbin/nologin".
  14. Finally left Centos and gave Ubuntu a try. Also, changed from apache to nginix. All went much easier than Centos but have one issue. I wish to have a non-human user dedicated to each website which PHP will run under and postgresql will use. I created my user but didn't provide a home directory (useradd -M abtfile). My configuration is shown below and phpinfo shows abtfile as the user but /home/abtfile as the home. I am now thinking I should have created a home for the user should keys or similar be needed for it, and think my options are: Home directory: /home/abtfile Host site: /var/www/abtfile/public. Doesn't seem right. Home directory: /home/abtfile Host site: /home/abtfile/public. Better but not sure. Home directory: /var/www/abtfile Host site: /var/www/abtfile/public. Likely but not sure. Questions. Should abtfile user have a home directory? Which of my three options or some other approach should be used? Do I define the home directory location the same way as for any linux user or must it also be defined under some php or nginix config file? Thanks! PS. Not having issues (yet), however, if you see any issues under my below configuration files, please let me know. /etc/php/8.1/fpm/pool.d/abtfile.conf [abtfile] user = abtfile group = abtfile ; Call whatever I want. Use ls -l /run/php/ to see existing sockets. listen = /var/run/php8.1-fpm-abtfile.sock ; Must match to the user and group on which NGINX is running listen.owner = www-data listen.group = www-data ; Consider changing below valves. ; mandatory pm = dynamic pm.max_children = 5 pm.min_spare_servers = 1 pm.max_spare_servers = 3 ; Use default values. ; pm.start_servers = 2 ; pm.max_spawn_rate = 32 ; pm.process_idle_timeout = 10s ; Not sure if necessary or correct ; Allows to set custom php configuration values. ; php_admin_value[disable_functions] = exec,passthru,shell_exec,system ; Allows to set PHP boolean flags ; php_admin_flag[allow_url_fopen] = off ; Add environmental data if desired. ; env[HOSTNAME] = $HOSTNAME ; env[TMP] = /tmp /etc/nginx/sites-available/abtfile server { server_name abtfile.testing.com; listen 80; listen [::]:80; root /var/www/abtfile/public; index index.php index.html index.htm; access_log /var/log/nginx/abtfile-access.log; error_log /var/log/nginx/abtfile-error.log; location / { try_files $uri $uri/ =404; } location ~ \.php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:/var/run/php8.1-fpm-abtfile.sock; fastcgi_split_path_info ^(.+\.php)(/.+)$; # What does this do? #fastcgi_index index.php; # Causes error. Maybe remove from above? } } /etc/nginx/php_fastcgi.conf try_files $fastcgi_script_name =404; include fastcgi_params; fastcgi_pass unix:/run/php/php-fpm.sock; fastcgi_index index.php; fastcgi_buffers 8 16k; fastcgi_buffer_size 32k; fastcgi_hide_header X-Powered-By; fastcgi_hide_header X-CF-Powered-By;
  15. Ended up forking the git repo, adding it as a repository in composer.json, and manually adding 'hautelook/alice-bundle": "2.10" to require.dev.
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.