Symfony2 / Doctrine2: Как переопределить класс DebugStack, используемый в доктрине DataCollector?

Чтобы лучше управлять тем, как я делал свои вызовы в базе данных с помощью Doctrine, я исследовал, как добавить php debug_backtrace в symfony doctrine db collector .

Мне удалось определить изменения, которые мне пришлось выполнить, но я еще не нашел хороший способ переопределить класс DebugStack (помимо переопределения класса поставщика).

класс находится здесь:

vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/DebugStack.php 

Для тех, кто заинтересован, вот результат:

введите описание изображения здесь

Для этого вам необходимо:

Переопределить vendor/doctrine/doctrine-bundle/Resources/views/Collector/db.html.twig

Скопируйте этот файл в app\resources\DoctrineBundle\views\Collector\db.html.twig

Вы увидите мои изменения ниже двух ==== MODIFIED === comments :

 {% extends app.request.isXmlHttpRequest ? 'WebProfilerBundle:Profiler:ajax_layout.html.twig' : 'WebProfilerBundle:Profiler:layout.html.twig' %} {% block toolbar %} {% set icon %} <img width="20" height="28" alt="Database" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAcCAYAAABh2p9gAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQRJREFUeNpi/P//PwM1ARMDlcGogZQDlpMnT7pxc3NbA9nhQKxOpL5rQLwJiPeBsI6Ozl+YBOOOHTv+AOllQNwtLS39F2owKYZ/gRq8G4i3ggxEToggWzvc3d2Pk+1lNL4fFAs6ODi8JzdS7mMRVyDVoAMHDsANdAPiOCC+jCQvQKqBQB/BDbwBxK5AHA3E/kB8nKJkA8TMQBwLxaBIKQbi70AvTADSBiSadwFXpCikpKQU8PDwkGTaly9fHFigkaKIJid4584dkiMFFI6jkTJII0WVmpHCAixZQEXWYhDeuXMnyLsVlEQKI45qFBQZ8eRECi4DBaAlDqle/8A48ip6gAADANdQY88Uc0oGAAAAAElFTkSuQmCC" /> <span class="sf-toolbar-status{% if 50 < collector.querycount %} sf-toolbar-status-yellow{% endif %}">{{ collector.querycount }}</span> {% if collector.querycount > 0 %} <span class="sf-toolbar-info-piece-additional-detail">in {{ '%0.2f'|format(collector.time * 1000) }} ms</span> {% endif %} {% if collector.invalidEntityCount > 0 %} <span class="sf-toolbar-info-piece-additional sf-toolbar-status sf-toolbar-status-red">{{ collector.invalidEntityCount }}</span> {% endif %} {% if collector.cacheEnabled %} <span class="sf-toolbar-info-piece-additional sf-toolbar-status sf-toolbar-status-green" title="Second level cache enabled">2l cache</span> {% endif %} {% endset %} {% set text %} <div class="sf-toolbar-info-piece"> <b>DB Queries</b> <span>{{ collector.querycount }}</span> </div> <div class="sf-toolbar-info-piece"> <b>Query time</b> <span>{{ '%0.2f'|format(collector.time * 1000) }} ms</span> </div> <div class="sf-toolbar-info-piece"> <b>Invalid entities</b> <span class="sf-toolbar-status sf-toolbar-status-{{ collector.invalidEntityCount > 0 ? 'red' : 'green' }}">{{ collector.invalidEntityCount }}</span> </div> {% if collector.cacheEnabled %} <div class="sf-toolbar-info-piece"> <b>Cache hits</b> <span class="sf-toolbar-status sf-toolbar-status-green">{{ collector.cacheHitsCount }}</span> </div> <div class="sf-toolbar-info-piece"> <b>Cache misses</b> <span class="sf-toolbar-status sf-toolbar-status-{{ collector.cacheMissesCount > 0 ? 'yellow' : 'green' }}">{{ collector.cacheMissesCount }}</span> </div> <div class="sf-toolbar-info-piece"> <b>Cache puts</b> <span class="sf-toolbar-status sf-toolbar-status-{{ collector.cachePutsCount > 0 ? 'yellow' : 'green' }}">{{ collector.cachePutsCount }}</span> </div> {% endif %} {% endset %} {% include 'WebProfilerBundle:Profiler:toolbar_item.html.twig' with { 'link': profiler_url } %} {% endblock %} {% block menu %} <span class="label"> <span class="icon"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAcCAYAAAB/E6/TAAABLUlEQVR42u3TP0vDQBiA8UK/gDiLzi0IhU4OEunk5OQUAhGSOBUCzqWfIKSzX8DRySF0URCcMjWLIJjFD9Cpk/D6HITecEPUuzhIAz8CIdyTP/f2iqI4qaqqDx8l5Ic2uIeP/bquezCokOAFF+oCN3t4gPzSEjc4NEPaCldQbzjELTYW0RJzHDchwwem+ons6ZBpLSJ7nueJC22h0V+FzmwWV0ee59vQNV67CGVZJmEYbkNjfpY6X6I0Qo4/3RMmTdDDspuQVsJvgkP3IdMbIkIjLPBoadG2646iKJI0Ta2wxm6OdnP0/Tk6DYJgHcfxpw21RtscDTDDnaVZ26474GkkSRIrrPEv5sgMTfHe+cA2O6wPH6vOBpYQNALneHb96XTEDI6dzpEZ0VzO0Rf3pP5LMLI4tAAAAABJRU5ErkJggg==" alt="" /></span> <strong>Doctrine</strong> <span class="count"> <span>{{ collector.querycount }}</span> <span>{{ '%0.0f'|format(collector.time * 1000) }} ms</span> </span> </span> {% endblock %} {% block panel %} {% if 'explain' == page %} {{ render(controller('DoctrineBundle:Profiler:explain', { 'token': token, 'panel': 'db', 'connectionName': app.request.query.get('connection'), 'query': app.request.query.get('query') })) }} {% else %} {{ block('queries') }} {% endif %} {% endblock %} {% block queries %} <h2>Queries</h2> {# ========================================= MODIFIED ===============================#} <script src="{{ asset('vendor/jquery/jquery-2.1.1.min.js') }}"></script> <style rel="stylesheet"> .hidden {display: none;} </style> <script> $(function(){ $('.trace').on('click', function(e){ e.preventDefault(); $(this).next().toggleClass('hidden'); }); }); </script> {% for connection, queries in collector.queries %} <h3>Connection <em>{{ connection }}</em></h3> {% if queries is empty %} <p> <em>No queries.</em> </p> {% else %} <p> <button type="button" class="sf-button" onclick="expandAllQueries(this);" data-action="expand"> <span class="border-l"> <span class="border-r"> <span class="btn-bg">Expand all queries</span> </span> </span> </button> </p> <table class="alt" id="queriesPlaceholder-{{ loop.index }}"> <thead> <tr> <th onclick="javascript:sortTable(this, 0, 'queries-{{ loop.index }}')" data-sort-direction="-1" style="cursor: pointer;">#<span>▲</span></th> <th onclick="javascript:sortTable(this, 1, 'queries-{{ loop.index }}')" style="cursor: pointer;">Time<span></span></th> <th style="width: 100%;">Info</th> </tr> </thead> <tbody id="queries-{{ loop.index }}"> {% for i, query in queries %} <tr id="queryNo-{{ i }}-{{ loop.parent.loop.index }}" class="{{ cycle(['odd', 'even'], i) }}"> <td>{{ loop.index }}</td> <td>{{ '%0.2f'|format(query.executionMS * 1000) }}&nbsp;ms</td> <td> <div class="query-section" data-state="collapsed" onclick="return expandQuery(this);" title="Expand query" data-target-id="code-{{ i }}-{{ loop.parent.loop.index }}" style="cursor: pointer;"> <img alt="+" src="data:image/gif;base64,R0lGODlhEgASAMQTANft99/v+Ga44bHb8ITG52S44dXs9+z1+uPx+YvK6WC24G+944/M6W28443L6dnu+Ge54v/+/l614P///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAABMALAAAAAASABIAQAVS4DQBTiOd6LkwgJgeUSzHSDoNaZ4PU6FLgYBA5/vFID/DbylRGiNIZu74I0h1hNsVxbNuUV4d9SsZM2EzWe1qThVzwWFOAFCQFa1RQq6DJB4iIQA7" style="display: inline;" /> <img alt="-" src="data:image/gif;base64,R0lGODlhEgASAMQSANft94TG57Hb8GS44ez1+mC24IvK6ePx+Wa44dXs92+942e54o3L6W2844/M6dnu+P/+/l614P///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAABIALAAAAAASABIAQAVCoCQBTBOd6Kk4gJhGBCTPxysJb44K0qD/ER/wlxjmisZkMqBEBW5NHrMZmVKvv9hMVsO+hE0EoNAstEYGxG9heIhCADs=" style="display: none;" /> <span style="display: none">Shrink query</span> <span id="smallcode-{{ i }}-{{ loop.parent.loop.index }}"> {{ query.sql|doctrine_minify_query|raw }} </span> </div> <code id="code-{{ i }}-{{ loop.parent.loop.index }}"> {{ query.sql|doctrine_pretty_query(i, loop.parent.loop.index)|raw }} </code> <span id="original-query-{{ i }}-{{ loop.parent.loop.index }}" style="display: none;"> {{ query.sql|doctrine_replace_query_parameters(query.params)|raw }} </span> <small> <strong>Parameters</strong>: {{ query.params|yaml_encode }} <br /> [<span id="expandParams-{{ i }}-{{ loop.parent.loop.index }}" onclick="javascript:toggleRunnableQuery(this);" target-data-id="original-query-{{ i }}-{{ loop.parent.loop.index }}" style="cursor: pointer;">Display runnable query</span>]<br/> </small> {% if query.explainable %} [<a href="{{ path('_profiler', {'panel': 'db', 'token': token, 'page': 'explain', 'connection': connection, 'query': i}) }}" onclick="return explain(this);" style="text-decoration: none;" title="Explains the query" data-target-id="explain-{{ i }}-{{ loop.parent.loop.index }}" > <img alt="+" src="data:image/gif;base64,R0lGODlhEgASAMQTANft99/v+Ga44bHb8ITG52S44dXs9+z1+uPx+YvK6WC24G+944/M6W28443L6dnu+Ge54v/+/l614P///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAABMALAAAAAASABIAQAVS4DQBTiOd6LkwgJgeUSzHSDoNaZ4PU6FLgYBA5/vFID/DbylRGiNIZu74I0h1hNsVxbNuUV4d9SsZM2EzWe1qThVzwWFOAFCQFa1RQq6DJB4iIQA7" style="display: inline; width: 12px; height: 12px;" /> <img alt="-" src="data:image/gif;base64,R0lGODlhEgASAMQSANft94TG57Hb8GS44ez1+mC24IvK6ePx+Wa44dXs92+942e54o3L6W2844/M6dnu+P/+/l614P///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAABIALAAAAAASABIAQAVCoCQBTBOd6Kk4gJhGBCTPxysJb44K0qD/ER/wlxjmisZkMqBEBW5NHrMZmVKvv9hMVsO+hE0EoNAstEYGxG9heIhCADs=" style="display: none; width: 12px; height: 12px;" /> <span style="vertical-align:top">Explain query</span> </a>] {% else %} This query cannot be explained {% endif %} {% if query.explainable %} <div id="explain-{{ i }}-{{ loop.parent.loop.index }}" class="loading"></div> {% endif %} {# ========================================= MODIFIED ===============================#} {% if query.trace is defined %} <div> [<a href="#/" style="text-decoration: none;" title="Show the trace" class="trace"> <img alt="+" src="data:image/gif;base64,R0lGODlhEgASAMQTANft99/v+Ga44bHb8ITG52S44dXs9+z1+uPx+YvK6WC24G+944/M6W28443L6dnu+Ge54v/+/l614P///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAABMALAAAAAASABIAQAVS4DQBTiOd6LkwgJgeUSzHSDoNaZ4PU6FLgYBA5/vFID/DbylRGiNIZu74I0h1hNsVxbNuUV4d9SsZM2EzWe1qThVzwWFOAFCQFa1RQq6DJB4iIQA7" style="display: inline; width: 12px; height: 12px;" /> <span style="vertical-align:top">Show the trace</span> </a>] <div class="hidden">{{ dump(query.trace) }}</div> </div> {% endif %} </td> </tr> {% endfor %} </tbody> </table> {% endif %} {% endfor %} <h2>Database Connections</h2> {% if collector.connections %} {% include 'WebProfilerBundle:Profiler:table.html.twig' with {data: collector.connections} only %} {% else %} <p> <em>No connections.</em> </p> {% endif %} <h2>Entity Managers</h2> {% if collector.managers %} {% include 'WebProfilerBundle:Profiler:table.html.twig' with {data: collector.managers} only %} {% else %} <p> <em>No entity managers.</em> </p> {% endif %} <h2>Second Level Cache</h2> {% if collector.cacheCounts %} {% include 'WebProfilerBundle:Profiler:table.html.twig' with {data: collector.cacheCounts} only %} {% if collector.cacheRegions.hits %} <h3>Number of cache hits</h3> {% include 'WebProfilerBundle:Profiler:table.html.twig' with {data: collector.cacheRegions.hits} only %} {% endif %} {% if collector.cacheRegions.misses %} <h3>Number of cache misses</h3> {% include 'WebProfilerBundle:Profiler:table.html.twig' with {data: collector.cacheRegions.misses} only %} {% endif %} {% if collector.cacheRegions.puts %} <h3>Number of cache puts</h3> {% include 'WebProfilerBundle:Profiler:table.html.twig' with {data: collector.cacheRegions.puts} only %} {% endif %} {% else %} <p> <em>No cache.</em> </p> {% endif %} <h2>Mapping</h2> {% for manager, classes in collector.entities %} <h3>Manager <em>{{ manager }}</em></h3> {% if classes is empty %} <p><em>No loaded entities.</em></p> {% else %} <table> <thead> <tr> <th scope="col">Class</th> <th scope="col">Mapping errors</th> </tr> </thead> <tbody> {% for class in classes %} <tr> <td>{{ class }}</td> <td> {% if collector.mappingErrors[manager][class] is defined %} <ul> {% for error in collector.mappingErrors[manager][class] %} <li>{{ error }}</li> {% endfor %} </ul> {% else %} Valid {% endif %} </td> </tr> {% endfor %} </tbody> </table> {% endif %} {% endfor %} <script type="text/javascript">//<![CDATA[ function explain(link) { "use strict"; var imgs = link.children, target = link.getAttribute('data-target-id'); Sfjs.toggle(target, imgs[0], imgs[1]) .load( target, link.href, null, function(xhr, el) { el.innerHTML = 'An error occurred while loading the details'; Sfjs.removeClass(el, 'loading'); } ); return false; } function expandAllQueries(button) { var queries = document.getElementsByClassName('query-section'), i = queries.length, action = button.getAttribute('data-action'); if (action == 'expand') { button.getElementsByClassName('btn-bg')[0].innerHTML = 'Collapse all queries'; while (i--) { if (queries[i].getAttribute('data-state') == 'collapsed') { expandQuery(queries[i]); } } } else { button.getElementsByClassName('btn-bg')[0].innerHTML = 'Expand all queries'; while (i--) { if (queries[i].getAttribute('data-state') == 'expanded') { expandQuery(queries[i]); } } } button.setAttribute('data-action', action == 'expand' ? 'collapse' : 'expand'); } function expandQuery(link) { var sections = link.children, target = link.getAttribute('data-target-id'), targetId = target.replace('code', ''), queriesParameters = document.getElementById('original-query' + targetId); if (queriesParameters.style.display != 'none') { queriesParameters.style.display = 'none'; document.getElementById('small' + target).style.display = 'inline'; document.getElementById('expandParams' + targetId).innerHTML = 'Display runnable query'; } if (document.getElementById('small' + target).style.display != 'none') { document.getElementById('small' + target).style.display = 'none'; document.getElementById(target).style.display = 'inline'; sections[0].style.display = 'none'; sections[1].style.display = 'inline'; sections[2].style.display = 'inline'; link.setAttribute('data-state', 'expanded'); } else { document.getElementById('small' + target).style.display = 'inline'; document.getElementById(target).style.display = 'none'; sections[0].style.display = 'inline'; sections[1].style.display = 'none'; sections[2].style.display = 'none'; link.setAttribute('data-state', 'collapsed'); } return false; } function toggleRunnableQuery(target) { var targetId = target.getAttribute('target-data-id').replace('original-query', ''), targetElement = document.getElementById(target.getAttribute('target-data-id')), elem; if (targetElement.style.display != 'block') { targetElement.style.display = 'block'; target.innerHTML = 'Hide runnable query'; document.getElementById('smallcode' + targetId).style.display = 'none'; document.getElementById('code' + targetId).style.display = 'none'; elem = document.getElementById('code' + targetId).parentElement.children[0]; elem.children[0].style.display = 'inline'; elem.children[1].style.display = 'none'; elem.children[2].style.display = 'none'; } else { targetElement.style.display = 'none'; target.innerHTML = 'Display runnable query'; document.getElementById('smallcode' + targetId).style.display = 'inline'; } } function sortTable(header, column, targetId) { "use strict"; var direction = parseInt(header.getAttribute('data-sort-direction')) || 1, items = [], target = document.getElementById(targetId), rows = target.children, headers = header.parentElement.children, i; for (i = 0; i < rows.length; ++i) { items.push(rows[i]); } for (i = 0; i < headers.length; ++i) { headers[i].removeAttribute('data-sort-direction'); if (headers[i].children.length > 0) { headers[i].children[0].innerHTML = ''; } } header.setAttribute('data-sort-direction', (-1*direction).toString()); header.children[0].innerHTML = direction > 0 ? '▲' : '▼'; items.sort(function(a, b) { return direction*(parseFloat(a.children[column].innerHTML) - parseFloat(b.children[column].innerHTML)); }); for (i = 0; i < items.length; ++i) { Sfjs.removeClass(items[i], i % 2 ? 'even' : 'odd'); Sfjs.addClass(items[i], i % 2 ? 'odd' : 'even'); target.appendChild(items[i]); } } //]]></script> <style> h3 { margin-bottom: 0px; } code { display: none; } code pre { padding: 5px; } </style> {% endblock %} 

Затем отредактируйте файл vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/DebugStack.php

и измените метод startQuery следующим образом:

 /** * {@inheritdoc} */ public function startQuery($sql, array $params = null, array $types = null) { if ($this->enabled) { $this->start = microtime(true); $this->queries[++$this->currentQuery] = array('sql' => $sql, 'params' => $params, 'types' => $types, 'executionMS' => 0, 'trace' => debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS)); } } 

Я еще не нашел, как сделать более эффективные изменения, поэтому каждое предложение здесь будет очень красивым! Благодаря !