phpmyadmin relation links

When browsing data, adds a link to foreign key values to open the related row. When looking at the table structure, highlights index fields and adds a link to the related table for foreign keys

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

// ==UserScript==
// @name           phpmyadmin relation links
// @author         ameboide
// @namespace      http://userscripts.org/scripts/show/118889
// @description    When browsing data, adds a link to foreign key values to open the related row. When looking at the table structure, highlights index fields and adds a link to the related table for foreign keys
// @version        1.01
// @include        */phpmyadmin/*
// ==/UserScript==
function $(q, elem){
	if(!elem) elem = document;
	return elem.querySelector(q);
}

function $$(q, elem){
	if(!elem) elem = document;
	return elem.querySelectorAll(q);
}

var urls = ['tbl_change', 'tbl_select'];
for(var i=0; i<urls.length; i++){
	var omnipresente = urls[i];
	var urlBase = $('[href*="'+omnipresente+'.php?"]');
	if(urlBase) break;
}
if(urlBase){
	urlBase = urlBase.href;

	//retorna el ancestro mas cercano de ese tipo
	function ancestro(nodo, tag){
		while(nodo && nodo.nodeName != tag.toUpperCase()) nodo = nodo.parentNode;
		return nodo;
	}

	//usa los datos de las FK para linkear (primero con lo cacheado, despues actualiza el cache)
	function ejecutar(rellenar){
		//si ya habia visitado esta tabla, tengo las claves foraneas guardadas
		var codigo_cache = document.location.host + ' : ' +
			urlBase.match(/(\?|&)db=(\w+)/)[2] + ' . ' +
			urlBase.match(/(\?|&)table=(\w+)/)[2];

		var cache = GM_getValue(codigo_cache, null);
		try{ if(cache) rellenar(JSON.parse(cache)); }
		catch(e){}

		//leer la pag de las claves foraneas (si estaba cacheado, leo igual para actualizar)
		var xhr = new XMLHttpRequest();
		xhr.onreadystatechange = function(){
			if (xhr.readyState == 4 && xhr.status == 200){
				try{
					var div = document.createElement('div');
					div.innerHTML = xhr.responseText;

					var claves = {};

					var opts = $$('select[name^="destination"] option[selected]', div);
					for(var i=0; i<opts.length; i++){
						var opt = opts[i];
						var tr = ancestro(opt, 'tr');

						var fk = opt.value.replace(/`/g, '').split('.');
						var ondel = $('select[name^="on_delete"] option[selected]', tr);
						var onup = $('select[name^="on_update"] option[selected]', tr);

						claves[tr.firstElementChild.textContent.trim()] = {
							db: fk[0],
							tabla: fk[1],
							campo: fk[2],
							ondel: ondel ? ondel.value : '',
							onup: onup ? onup.value : ''
						};
					}

					var claves_str = JSON.stringify(claves);
					//si no es lo mismo q tenia cacheado, vuelvo a rellenar con la info nueva
					if(claves_str != cache){
						//workaround para error raro
						setTimeout(function() {
							GM_setValue(codigo_cache, claves_str);
						}, 0);
						rellenar(claves);
					}
				}
				catch(e){}
			}
		}
		xhr.open('GET', urlBase.replace(omnipresente, 'tbl_relation'), true);
		xhr.send(null);
	}

	var listaCampos = $$('#tablestructure tbody th');
	if(listaCampos.length){ //estoy en la estructura de la tabla
		//tds_campos['nombre_del_campo'] = td
		var tds_campos = {};
		for(var i=0; i<listaCampos.length; i++){
			var campo = listaCampos[i];
			var txt = campo.textContent.trim();
			tds_campos[txt] = campo;

			//id sin indice -> rojo claro / codigo sin indice -> rojo oscuro
			if(txt.match(/^id_|_id$/i) || txt.match(/^Id[A-Z]|[A-Z]Id$/)) campo.style.color = '#f00';
			else if(txt.match(/^codigo_|_codigo$|^codigo$/i) || txt.match(/^Codigo[A-Z]|[A-Z]Codigo$|Cod[A-Z]|[A-Z]Cod/)) campo.style.color = '#800';
		}

		//idx -> azul oscuro
		var idxs = $$('#table_indexes tbody tr:not(.tblFooters) td:last-of-type');
		if(!idxs.length){
			var edit = $('[href^="tbl_indexes.php"]');
			if(edit) idxs = $$('td:nth-of-type(6)', ancestro(edit, 'tr').parentNode);
		}
		for(i=0; i<idxs.length; i++){
			var campo = tds_campos[idxs[i].textContent.trim()];
			if(campo) campo.style.color = '#008';
		}

		var matches = $('a.tabactive').href.match(/\/(\w+\.php).*token=(\w+)/);
		var url = matches[1];
		var token = matches[2];

		//FK -> link azul claro
		ejecutar(function(claves){
			for(var campo in claves){
				if(!tds_campos[campo]) continue;
				var fk = claves[campo];

				var a = document.createElement('a');
				a.href = url+'?db='+fk.db+'&token='+token+'&table='+fk.tabla;
				a.title = fk.tabla+' . '+fk.campo+
					(fk.ondel?' [DEL: '+fk.ondel+']':'')+
					(fk.onup?' [UP: '+fk.onup+'] ':'');
				a.id = 'link_fk_' + campo;
				a.innerHTML = ' &gt; ['+fk.tabla+
					(fk.ondel?' D:'+fk.ondel[0]:'')+
					(fk.onup?' U:'+fk.onup[0]:'')+']';
				a.style.cssFloat = 'left';
				a.style.fontSize = 'xx-small';

				var link_viejo = document.getElementById(a.id);
				if(link_viejo) tds_campos[campo].removeChild(link_viejo);

				tds_campos[campo].appendChild(a);
				tds_campos[campo].style.color = '#00f';
			}
		});
	}
	else if($('#table_results')){ //estoy viendo los datos
		//indices['nombre_campo'] = indice_del_td_con_el_valor
		var indices = {};
		var ths = $$('th', $('#table_results th:not([colspan])').parentNode);
		for(var i=0, idx=0; i<ths.length; i++){
			var th = ths[i];
			var colspan = th.getAttribute('colspan');
			if(colspan){
				idx += parseInt(colspan);
				continue;
			}

			var a = $('a', th);
			var campo = a ? a : th.childNodes[0];
			indices[campo.textContent.trim()] = ++idx;
		}

		//FK -> link a un select * from fk.tabla where fk.campo = valor
		ejecutar(function(claves){
			for(var campo in claves){
				var idx = indices[campo];
				if(!idx) continue;
				var fk = claves[campo];

				var url = urlBase.replace(omnipresente, 'sql')
					.replace(/(db=)\w+/, '$1'+fk.db)
					.replace(/(table=)\w+/, '$1'+fk.tabla) +
					'&show_query=1&sql_query=' +
					encodeURI("SELECT * FROM "+fk.tabla+" WHERE "+fk.campo+" = '-valor-'");

				//reemplazar los valores por links q entregan la fila asociada
				var tds = $$('#table_results td:nth-of-type('+idx+')');
				for(var j=0; j<tds.length; j++){
					var td = tds[j];
					if(td.innerHTML.trim() == '<i>NULL</i>') continue;

					td.innerHTML = '<a href="'+url.replace('-valor-', td.textContent)+
						'" title="'+fk.tabla+' . '+fk.campo+'">'+td.textContent+'</a>';
				}
			}
		});
	}
}