private void buildPropertyMap(QueryBuilderScopeContext qb) {

    if (!collectProperties) {
      return;
    }

    for (QueryBuilderJoin join : qb.getJoinMap().values()) {
      String className = join.getResolvedClass();
      if (className != null) {
        PhpClass phpClass = PhpElementsUtil.getClassInterface(project, className);
        if (phpClass != null) {
          qb.addPropertyAlias(
              join.getAlias(),
              new QueryBuilderPropertyAlias(
                  join.getAlias(),
                  null,
                  new DoctrineModelField(join.getAlias())
                      .addTarget(phpClass)
                      .setTypeName(phpClass.getPresentableFQN())));

          // add entity properties
          for (DoctrineModelField field : EntityHelper.getModelFields(phpClass)) {
            qb.addPropertyAlias(
                join.getAlias() + "." + field.getName(),
                new QueryBuilderPropertyAlias(join.getAlias(), field.getName(), field));
          }
        }
      }
    }
  }
  private void collectSelects(
      QueryBuilderScopeContext qb, MethodReference methodReference, String name) {

    if (!Arrays.asList("select", "addSelect").contains(name)) {
      return;
    }

    // $qb->select('foo')
    PsiElement psiElement = PsiElementUtils.getMethodParameterPsiElementAt(methodReference, 0);
    String literalValue = PhpElementsUtil.getStringValue(psiElement);
    if (literalValue != null) {
      qb.addSelect(literalValue);
      return;
    }

    // $qb->select(array('foo', 'bar', 'accessoryDetail'))
    if (psiElement instanceof ArrayCreationExpression) {
      for (PsiElement arrayValue :
          PsiElementUtils.getChildrenOfTypeAsList(
              psiElement, PlatformPatterns.psiElement(PhpElementTypes.ARRAY_VALUE))) {
        if (arrayValue.getChildren().length == 1) {
          String arrayValueString = PhpElementsUtil.getStringValue(arrayValue.getChildren()[0]);
          if (arrayValueString != null) {
            qb.addSelect(arrayValueString);
          }
        }
      }
    }
  }
  private void collectJoins(
      QueryBuilderScopeContext qb, MethodReference methodReference, String name) {

    if (!collectJoins
        || !Arrays.asList("join", "leftJoin", "rightJoin", "innerJoin").contains(name)) {
      return;
    }

    String join = PsiElementUtils.getMethodParameterAt(methodReference, 0);
    String alias = PsiElementUtils.getMethodParameterAt(methodReference, 1);
    if (join != null && alias != null) {
      qb.addJoin(alias, new QueryBuilderJoin(join, alias));
    }
  }
  private void collectSelectInForm(
      QueryBuilderScopeContext qb, MethodReference methodReference, String name) {

    // $qb->from('foo', 'select')
    if (!"from".equals(name)) {
      return;
    }

    PsiElement psiElement = PsiElementUtils.getMethodParameterPsiElementAt(methodReference, 1);
    String literalValue = PhpElementsUtil.getStringValue(psiElement);
    if (literalValue != null) {
      qb.addSelect(literalValue);
    }
  }
  private void collectParameter(
      QueryBuilderScopeContext qb, MethodReference methodReference, String name) {

    if (!collectParameter || !Arrays.asList("where", "andWhere").contains(name)) {
      return;
    }

    String value = PsiElementUtils.getMethodParameterAt(methodReference, 0);
    if (value != null) {
      Matcher matcher = Pattern.compile(":(\\w+)", Pattern.MULTILINE).matcher(value);
      while (matcher.find()) {
        qb.addParameter(matcher.group(1));
      }
    }
  }
  public QueryBuilderScopeContext collect() {
    QueryBuilderScopeContext qb = new QueryBuilderScopeContext();

    // doctrine needs valid root with an alias, try to find one in method references or scope
    Map<String, String> map = this.findRootDefinition(methodReferences);
    if (map.size() > 0) {
      Map.Entry<String, String> entry = map.entrySet().iterator().next();
      qb.addTable(entry.getKey(), entry.getValue());
    }

    for (MethodReference methodReference : methodReferences) {

      String name = methodReference.getName();
      if (name != null) {
        collectParameter(qb, methodReference, name);
        collectJoins(qb, methodReference, name);
        collectSelects(qb, methodReference, name);
        collectSelectInForm(qb, methodReference, name);
      }
    }

    // first tableMap entry is root, we add several initial data
    if (qb.getTableMap().size() > 0) {
      Map.Entry<String, String> entry = qb.getTableMap().entrySet().iterator().next();
      String className = entry.getKey();
      PhpClass phpClass = PhpElementsUtil.getClassInterface(project, className);

      // add root select fields
      if (phpClass != null) {

        qb.addPropertyAlias(
            entry.getValue(),
            new QueryBuilderPropertyAlias(
                entry.getValue(),
                null,
                new DoctrineModelField(entry.getValue())
                    .addTarget(phpClass)
                    .setTypeName(phpClass.getPresentableFQN())));

        List<QueryBuilderRelation> relationList = new ArrayList<QueryBuilderRelation>();

        // qb.addRelation(entry.getValue(), attachRelationFields(phpClass));
        for (DoctrineModelField field : EntityHelper.getModelFields(phpClass)) {
          qb.addPropertyAlias(
              entry.getValue() + "." + field.getName(),
              new QueryBuilderPropertyAlias(entry.getValue(), field.getName(), field));
          if (field.getRelation() != null && field.getRelationType() != null) {
            relationList.add(new QueryBuilderRelation(field.getName(), field.getRelation()));
          }
        }

        qb.addRelation(entry.getValue(), relationList);
      }

      QueryBuilderRelationClassResolver resolver =
          new QueryBuilderRelationClassResolver(
              project, entry.getValue(), entry.getKey(), qb.getRelationMap(), qb.getJoinMap());
      resolver.collect();
    }

    // we have a querybuilder which complete known elements now
    // se we can builder a property (field) map table from it
    this.buildPropertyMap(qb);

    return qb;
  }