kulekci.net kulekci.net      About      Speaking      Elasticsearch

Doctrine Mapped Super Class

If you want to extend an entity class and you dont want to create a new table on your db, you need to use MappedSuperClass.

For example, you want to use OAuth2 of PHP League on your system and you should create user model to implement server. But you have already create another user model, I mention as BaseUser, before for your own system. Of course You can use directly BaseUser model. In my view, this is wrong for modular system for two reason. The first reason is you can not seperate your server module from your base module. The second reason is, your BaseUser model will contain unnecessary methods about OAuth2. So, you should extend BaseUser for your OAuth module. In the first scenario, you can easily extend your BaseUser model and add external methods for OAuth2 systems.

Lets create a sample User Entity for this situation:

/**
 * @ORM\Entity()
 * @ORM\Table(name="user")
 */
class BaseUser
{

    /**
     * @var integer $id
     *
     * @ORM\Id
     * @ORM\Column(name="id", type="integer")
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    protected $id;

    /**
     * @var string
     * @ORM\Column(name="username", type="string", length=100)
     */
    protected $username;

    /**
     * Password
     *
     * @ORM\Column(name="password", type="string", length=150)
     * @var string
     */
    protected $password;
}

Now, lets create an OAuth model:

use League\OAuth2\Server\Entities\UserEntityInterface;
use YourModule\Entity\User as BaseUser;

/**
 * Class OAuth2User
 */
class OAuth2User extends BaseUser implements UserEntityInterface
{
    /**
     * Return the user's identifier. This method required extenal OAuth module 
     * but there is no getIdentifier method on my BaseUser model
     *
     * @return string
     */
    public function getIdentifier()
    {
        return $this->getUsername();
    }
}

Run the following commands to update our schema for doctrine:

php vendor/bin/doctrine orm:schema-tool:update --dump-sql

Lets look what the response will be. Doctrine gives you some errors about super class:

  [Doctrine\ORM\Mapping\MappingException]
  Class "OAuth2Server\Entity\User" sub class of "YourModule\Entity\User" is not a valid entity or mapped
  super class.

Doctrine say us, there is a class and it extends an Entity. Do something to solve this problem because I dont know how to use this class as an entity or what? There are some solution to solve this problem. First one is:

Mark your OAuth2User as basicly Entity class

/**
 * Class User
 *
 * @ORM\Entity()
 */
class User extends BaseUser implements UserEntityInterface
{
  // ...
}

In this scenario, Doctrine will create us a new User table. But we don’t want it. We want to use our BaseUser table as our OAuth2User table. We want to extend our DB table also, whe we extends our class. You should use MappedSuperclass feature of Doctrine like this:

/**
 * Class User
 *
 * @ORM\MappedSuperclass
 */
class User extends BaseUser implements UserEntityInterface
{
  // ...
}

Now, we can use User class as an entity and this entity also refer our BaseUser entity and our base user table.

Angular 2 Directive Error

For a while, I have been trying to learn AngularJS 2 and typescript. I really like them. Last day, an error occured which is “No Directive annotation found” while I was trying to create a directive and I don’t understand exception why throwing but I spent lots of time on it to solve. The error is :

members:16 Error: (SystemJS) No Directive annotation found on offClickDirective
  Error: No Directive annotation found on offClickDirective
      at new BaseException (http://localhost:3000/node_modules/@angular/compiler/bundles/compiler.umd.js:5116:27)
      at DirectiveResolver.resolve (http://localhost:3000/node_modules/@angular/compiler/bundles/compiler.umd.js:12790:23)
      at CompileMetadataResolver.getDirectiveMetadata (http://localhost:3000/node_modules/@angular/compiler/bundles/compiler.umd.js:13097:55)
      at addDirective (http://localhost:3000/node_modules/@angular/compiler/bundles/compiler.umd.js:13367:37)
      at eval (http://localhost:3000/node_modules/@angular/compiler/bundles/compiler.umd.js:13376:77)
      at Array.forEach (native)
      at CompileMetadataResolver._getTransitiveViewDirectivesAndPipes (http://localhost:3000/node_modules/@angular/compiler/bundles/compiler.umd.js:13376:41)
      at eval (http://localhost:3000/node_modules/@angular/compiler/bundles/compiler.umd.js:13348:78)
      at Array.forEach (native)
      at CompileMetadataResolver._verifyModule (http://localhost:3000/node_modules/@angular/compiler/bundles/compiler.umd.js:13348:43)
  Evaluating http://localhost:3000/dist/main.js
  Error loading http://localhost:3000/dist/main.js

I also imported necessary component and classes and this is my component file content:

import { Directive, Input, Host } from '@angular/core';

@Directive({
  selector: '[offClick]',
  //inputs: ['offClick'],
  host: {
    '(click)':'onClick($event)'
  }
});

export class offClickDirective {
  @Input('offClick') offClickHandler;
  
  constructor() {}

  ngOnInit() {
    let self=this;
    setTimeout(() => {document.addEventListener('click', self.offClickHandler);}, 0);
  }
  
  ngOnDestroy() {
    let self=this;
    document.removeEventListener('click', self.offClickHandler);    
  }

  closeAllDropdown($event) {
    if ($event.target.classList.contains('dropdown-toggle')) {
        return
      }
      const dropdowns = document.querySelectorAll('.dropdown')
      for (let i = 0; i < dropdowns.length; i++) {
        dropdowns[i].classList.remove('open')
      }
  }

  onClick($event) {
    this.closeAllDropdown($event);
  }
}

After that, I thought that I had to define something to somewhere for example systemjs.config.js, …etc. And I searched it on Google. I found lots of issues and lots of comments about this situation. But, in fact, they were not relevant. Because all of them, was talking about version difference or importing problems. I had already imported all the necessary libraries. Finally, I came accros this post and also found the solution, too. The problem is “;” (semicolon) as usually. I remove my semicolon after @Directive definition and problem solved. This is the last version of the @Directive part of my code.

@Directive({
  selector: '[offClick]',
  //inputs: ['offClick'],
  host: {
    '(click)':'onClick($event)'
  }
}) // removed semicolon was here

Git Submodule

I did not use git submodules in production. This article for only information. Sometimes it can be really good option.

Working more than a year on git, i like that and i want to share some experience from here about submodule.

Submodule is great idea to attach external repos to your project to a path. Now, we can try to create a lib directory and we put some submodules in lib’s subdirectiories. I am exampling to you how the submodules is adding to a repo.

Adding a Submodule to your repository

git submodule add https://github.com/headjs/headjs.git lib/js/headjs
git submodule add https://github.com/marcuswestin/store.js.git lib/js/storejs
git submodule add https://github.com/cebe/smarty.git lib/template-engine/smarty
git submodule add https://github.com/fabpot/Twig.git lib/template-engine/twig

We add submodules to our project now you can see your added library on your status

git status

All your modules writing on “.gitmodules” file which is created by git. You can see with following code your gitmodules file content.

cat .gitmodules

For me, .gitmodules file content is following

[submodule "lib/js/storejs"]
    path = lib/js/storejs
    url = git://github.com/marcuswestin/store.js.git
[submodule "lib/js/headjs"]
    path = lib/js/headjs
    url = git://github.com/headjs/headjs.git
[submodule "lib/template-engine/twig"]
    path = lib/template-engine/twig
    url = https://github.com/fabpot/Twig.git
[submodule "lib/template-engine/smarty"]
    path = lib/template-engine/smarty
    url = https://github.com/cebe/smarty.git

Removing Submodule

If you want, you also can remove some submodules from your project. You can use to remove one or more of your submodule(s).

git submodule rm lib/js/headjs

### Updating Submodule

There are some updates your submodule while you are working with your project. For example, Twig template engine release a new version. You can use to update your submodules following lines in to your submodule’s path.

git submodule update

Cloning a Project with Submodules

To clone a project with its submodules, you can use –recursive parameter of git. You can check following commands.

$ git clone --recursive git@github.com:hkulekci/git-submodule-test.git

That’s all for now. To more information, you can look references part.

References :